SPDY changes from server.

R=willchan
Review URL: http://codereview.chromium.org/8473007

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@111854 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/net/spdy/spdy_frame_builder.cc b/net/spdy/spdy_frame_builder.cc
index cb8b486..ace581d 100644
--- a/net/spdy/spdy_frame_builder.cc
+++ b/net/spdy/spdy_frame_builder.cc
@@ -111,30 +111,6 @@
   return ReadBytes(iter, data, *length);
 }
 
-bool SpdyFrameBuilder::WriteString(const std::string& value) {
-  if (value.size() > 0xffff)
-    return false;
-
-  if (!WriteUInt16(static_cast<int>(value.size())))
-    return false;
-
-  return WriteBytes(value.data(), static_cast<uint16>(value.size()));
-}
-
-bool SpdyFrameBuilder::WriteBytes(const void* data, uint16 data_len) {
-  DCHECK(capacity_ != kCapacityReadOnly);
-
-  char* dest = BeginWrite(data_len);
-  if (!dest)
-    return false;
-
-  memcpy(dest, data, data_len);
-
-  EndWrite(dest, data_len);
-  length_ += data_len;
-  return true;
-}
-
 char* SpdyFrameBuilder::BeginWriteData(uint16 length) {
   DCHECK_EQ(variable_buffer_offset_, 0U) <<
     "There can only be one variable buffer in a SpdyFrameBuilder";
@@ -155,6 +131,7 @@
 }
 
 char* SpdyFrameBuilder::BeginWrite(size_t length) {
+  size_t offset = length_;
   size_t needed_size = length_ + length;
   if (needed_size > capacity_ && !Resize(std::max(capacity_ * 2, needed_size)))
     return NULL;
@@ -163,12 +140,41 @@
   DCHECK_LE(length, std::numeric_limits<uint32>::max());
 #endif
 
-  return buffer_ + length_;
+  return buffer_ + offset;
 }
 
 void SpdyFrameBuilder::EndWrite(char* dest, int length) {
 }
 
+bool SpdyFrameBuilder::WriteBytes(const void* data, uint32 data_len) {
+  DCHECK(capacity_ != kCapacityReadOnly);
+
+  if (data_len > kLengthMask) {
+    return false;
+  }
+
+  char* dest = BeginWrite(data_len);
+  if (!dest)
+    return false;
+
+  memcpy(dest, data, data_len);
+
+  EndWrite(dest, data_len);
+  length_ += data_len;
+  return true;
+}
+
+bool SpdyFrameBuilder::WriteString(const std::string& value) {
+  if (value.size() > 0xffff)
+    return false;
+
+  if (!WriteUInt16(static_cast<int>(value.size())))
+    return false;
+
+  return WriteBytes(value.data(), static_cast<uint16>(value.size()));
+}
+
+// TODO(hkhalil) Remove Resize() entirely.
 bool SpdyFrameBuilder::Resize(size_t new_capacity) {
   if (new_capacity <= capacity_)
     return true;
@@ -180,6 +186,8 @@
     memcpy(p, buffer_, capacity_);
     delete[] buffer_;
   }
+  if (!p && new_capacity > 0)
+    return false;
   buffer_ = p;
   capacity_ = new_capacity;
   return true;
diff --git a/net/spdy/spdy_frame_builder.h b/net/spdy/spdy_frame_builder.h
index b3d2de7..b53adbc 100644
--- a/net/spdy/spdy_frame_builder.h
+++ b/net/spdy/spdy_frame_builder.h
@@ -28,6 +28,8 @@
 // track of the type of data written to it.
 class NET_EXPORT_PRIVATE SpdyFrameBuilder {
  public:
+  ~SpdyFrameBuilder();
+
   SpdyFrameBuilder();
 
   // Initiailizes a SpdyFrameBuilder with a buffer of given size.
@@ -40,8 +42,6 @@
   // this way.
   SpdyFrameBuilder(const char* data, int data_len);
 
-  ~SpdyFrameBuilder();
-
   // Returns the size of the SpdyFrameBuilder's data.
   int length() const { return length_; }
 
@@ -77,7 +77,7 @@
     return WriteBytes(&value, sizeof(value));
   }
   bool WriteString(const std::string& value);
-  bool WriteBytes(const void* data, uint16 data_len);
+  bool WriteBytes(const void* data, uint32 data_len);
 
   // Write an integer to a particular offset in the data buffer.
   bool WriteUInt32ToOffset(int offset, uint32 value) {
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc
index 29a1565..b0928dd 100644
--- a/net/spdy/spdy_framer.cc
+++ b/net/spdy/spdy_framer.cc
@@ -339,7 +339,10 @@
         continue;
       }
       default:
-        break;
+        // This ensures that we don't infinite-loop if state_ gets an
+        // invalid value somehow, such as due to a SpdyFramer getting deleted
+        // from a callback it calls.
+        goto bottom;
     }
   }
  bottom:
diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h
index abfa5234..1bbe355 100644
--- a/net/spdy/spdy_framer.h
+++ b/net/spdy/spdy_framer.h
@@ -61,7 +61,9 @@
   // Called if an error is detected in the SpdyFrame protocol.
   virtual void OnError(SpdyFramer* framer) = 0;
 
-  // Called when a control frame is received.
+  // Called when a control frame is received. Note that SYN_STREAM, SYN_REPLY,
+  // and HEADER control frames do not include the header block data.
+  // (See OnControlFrameHeaderData().)
   virtual void OnControl(const SpdyControlFrame* frame) = 0;
 
   // Called when a chunk of header data is available. This is called
@@ -327,8 +329,6 @@
   FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, ExpandBuffer_HeapSmash);
   FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, HugeHeaderBlock);
   FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, UnclosedStreamDataCompressors);
-  FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest,
-                           UncompressLargerThanFrameBufferInitialSize);
   friend class net::HttpNetworkLayer;  // This is temporary for the server.
   friend class net::HttpNetworkTransactionTest;
   friend class net::HttpProxyClientSocketPoolTest;
@@ -343,17 +343,6 @@
   friend void test::FramerSetEnableCompressionHelper(SpdyFramer* framer,
                                                      bool compress);
 
-  // The initial size of the control frame buffer; this is used internally
-  // as we parse through control frames. (It is exposed here for unit test
-  // purposes.)
-  // This is only used when compression is enabled; otherwise,
-  // kUncompressedControlFrameBufferInitialSize is used.
-  static size_t kControlFrameBufferInitialSize;
-
-  // The maximum size of the control frame buffer that we support.
-  // TODO(mbelshe): We should make this stream-based so there are no limits.
-  static size_t kControlFrameBufferMaxSize;
-
  private:
   typedef std::map<SpdyStreamId, z_stream*> CompressorMap;
 
@@ -363,8 +352,6 @@
   void ProcessControlFrameHeader();
   size_t ProcessControlFramePayload(const char* data, size_t len);
   size_t ProcessControlFrameBeforeHeaderBlock(const char* data, size_t len);
-  // TODO(hkhalil): Rename to ProcessControlFrameHeaderBlock once said flag is
-  // removed, replacing the old method.
   size_t NewProcessControlFrameHeaderBlock(const char* data, size_t len);
   size_t ProcessControlFrameHeaderBlock(const char* data, size_t len);
   size_t ProcessDataFramePayload(const char* data, size_t len);
@@ -435,6 +422,13 @@
   int num_stream_compressors() const { return stream_compressors_.size(); }
   int num_stream_decompressors() const { return stream_decompressors_.size(); }
 
+  // The initial size of the control frame buffer; this is used internally
+  // as we parse through control frames. (It is exposed here for unit test
+  // purposes.)
+  // This is only used when compression is enabled; otherwise,
+  // kUncompressedControlFrameBufferInitialSize is used.
+  static size_t kControlFrameBufferInitialSize;
+
   // The initial size of the control frame buffer when compression is disabled.
   // This exists because we don't do stream (de)compressed control frame data to
   // our visitor; we instead buffer the entirety of the control frame and then
@@ -448,6 +442,10 @@
   // implemented.
   static const size_t kUncompressedControlFrameBufferInitialSize = 18 + 16;
 
+  // The maximum size of the control frame buffer that we support.
+  // TODO(mbelshe): We should make this stream-based so there are no limits.
+  static size_t kControlFrameBufferMaxSize;
+
   // The size of the buffer into which compressed frames are inflated.
   static const size_t kDecompressionBufferSize = 8 * 1024;
 
diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc
index b17c66b..3547d95 100644
--- a/net/spdy/spdy_framer_test.cc
+++ b/net/spdy/spdy_framer_test.cc
@@ -20,6 +20,13 @@
   static const char kHexChars[] = "0123456789ABCDEF";
   static const int kColumns = 4;
 
+  const int kSizeLimit = 1024;
+  if (length > kSizeLimit || mark_length > kSizeLimit) {
+    LOG(ERROR) << "Only dumping first " << kSizeLimit << " bytes.";
+    length = std::min(length, kSizeLimit);
+    mark_length = std::min(mark_length, kSizeLimit);
+  }
+
   std::string hex;
   for (const unsigned char* row = data; length > 0;
        row += kColumns, length -= kColumns) {
@@ -76,27 +83,44 @@
       << HexDumpWithMarks(actual, actual_len, marks.get(), max_len);
 }
 
-void FramerSetEnableCompressionHelper(SpdyFramer* framer, bool compress) {
-  framer->set_enable_compression(compress);
-}
-
 class TestSpdyVisitor : public SpdyFramerVisitorInterface  {
  public:
+  static const size_t kDefaultHeaderBufferSize = 16 * 1024;
+
   TestSpdyVisitor()
-    : error_count_(0),
+    : use_compression_(false),
+      error_count_(0),
       syn_frame_count_(0),
       syn_reply_frame_count_(0),
       headers_frame_count_(0),
+      goaway_count_(0),
       data_bytes_(0),
       fin_frame_count_(0),
       fin_flag_count_(0),
-      zero_length_data_frame_count_(0) {
+      zero_length_data_frame_count_(0),
+      header_blocks_count_(0),
+      control_frame_header_data_count_(0),
+      zero_length_control_frame_header_data_count_(0),
+      data_frame_count_(0),
+      header_buffer_(new char[kDefaultHeaderBufferSize]),
+      header_buffer_length_(0),
+      header_buffer_size_(kDefaultHeaderBufferSize),
+      header_stream_id_(-1),
+      header_control_type_(NUM_CONTROL_FRAME_TYPES),
+      header_buffer_valid_(false) {
   }
 
   void OnError(SpdyFramer* f) {
+    LOG(INFO) << "SpdyFramer Error: "
+              << SpdyFramer::ErrorCodeToString(f->error_code());
     error_count_++;
   }
 
+  void OnDataFrameHeader(const SpdyDataFrame* frame) {
+    data_frame_count_++;
+    header_stream_id_ = frame->stream_id();
+  }
+
   void OnStreamFrameData(SpdyStreamId stream_id,
                          const char* data,
                          size_t len) {
@@ -114,29 +138,27 @@
   }
 
   void OnControl(const SpdyControlFrame* frame) {
-    SpdyHeaderBlock headers;
-    bool parsed_headers = false;
     switch (frame->type()) {
       case SYN_STREAM:
-        parsed_headers = framer_.ParseHeaderBlock(frame, &headers);
-        DCHECK(parsed_headers);
         syn_frame_count_++;
+        InitHeaderStreaming(frame);
         break;
       case SYN_REPLY:
-        parsed_headers = framer_.ParseHeaderBlock(frame, &headers);
-        DCHECK(parsed_headers);
         syn_reply_frame_count_++;
+        InitHeaderStreaming(frame);
         break;
       case RST_STREAM:
         fin_frame_count_++;
         break;
       case HEADERS:
-        parsed_headers = framer_.ParseHeaderBlock(frame, &headers);
-        DCHECK(parsed_headers);
         headers_frame_count_++;
+        InitHeaderStreaming(frame);
+        break;
+      case GOAWAY:
+        goaway_count_++;
         break;
       default:
-        DCHECK(false);  // Error!
+        DLOG(FATAL);  // Error!
     }
     if (frame->flags() & CONTROL_FLAG_FIN)
       ++fin_flag_count_;
@@ -145,17 +167,30 @@
   bool OnControlFrameHeaderData(SpdyStreamId stream_id,
                                 const char* header_data,
                                 size_t len) {
-    DCHECK(false);
-    return false;
-  }
-
-  void OnDataFrameHeader(const SpdyDataFrame* frame) {
-    DCHECK(false);
+    ++control_frame_header_data_count_;
+    CHECK_EQ(header_stream_id_, stream_id);
+    if (len == 0) {
+      ++zero_length_control_frame_header_data_count_;
+      // Indicates end-of-header-block.
+      CHECK(header_buffer_valid_);
+      bool parsed_headers = SpdyFramer::ParseHeaderBlockInBuffer(
+          header_buffer_.get(), header_buffer_length_, &headers_);
+      DCHECK(parsed_headers);
+      return true;
+    }
+    const size_t available = header_buffer_size_ - header_buffer_length_;
+    if (len > available) {
+      header_buffer_valid_ = false;
+      return false;
+    }
+    memcpy(header_buffer_.get() + header_buffer_length_, header_data, len);
+    header_buffer_length_ += len;
+    return true;
   }
 
   // Convenience function which runs a framer simulation with particular input.
   void SimulateInFramer(const unsigned char* input, size_t size) {
-    framer_.set_enable_compression(false);
+    framer_.set_enable_compression(use_compression_);
     framer_.set_visitor(this);
     size_t input_remaining = size;
     const char* input_ptr = reinterpret_cast<const char*>(input);
@@ -175,16 +210,56 @@
     }
   }
 
+  void InitHeaderStreaming(const SpdyControlFrame* frame) {
+    memset(header_buffer_.get(), 0, header_buffer_size_);
+    header_buffer_length_ = 0;
+    header_stream_id_ = SpdyFramer::GetControlFrameStreamId(frame);
+    header_control_type_ = frame->type();
+    header_buffer_valid_ = true;
+    DCHECK_NE(header_stream_id_, SpdyFramer::kInvalidStream);
+  }
+
+  // Override the default buffer size (16K). Call before using the framer!
+  void set_header_buffer_size(size_t header_buffer_size) {
+    header_buffer_size_ = header_buffer_size;
+    header_buffer_.reset(new char[header_buffer_size]);
+  }
+
+  static size_t control_frame_buffer_max_size() {
+    return SpdyFramer::kControlFrameBufferMaxSize;
+  }
+
+  static size_t header_data_chunk_max_size() {
+    return SpdyFramer::kHeaderDataChunkMaxSize;
+  }
+
   SpdyFramer framer_;
+  bool use_compression_;
+
   // Counters from the visitor callbacks.
   int error_count_;
   int syn_frame_count_;
   int syn_reply_frame_count_;
   int headers_frame_count_;
+  int goaway_count_;
   int data_bytes_;
   int fin_frame_count_;  // The count of RST_STREAM type frames received.
   int fin_flag_count_;  // The count of frames with the FIN flag set.
   int zero_length_data_frame_count_;  // The count of zero-length data frames.
+  int header_blocks_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_;
+  int data_frame_count_;
+
+  // Header block streaming state:
+  scoped_array<char> header_buffer_;
+  size_t header_buffer_length_;
+  size_t header_buffer_size_;
+  SpdyStreamId header_stream_id_;
+  SpdyControlType header_control_type_;
+  bool header_buffer_valid_;
+  SpdyHeaderBlock headers_;
 };
 
 }  // namespace test
@@ -200,12 +275,12 @@
 using spdy::SpdyHeaderBlock;
 using spdy::SpdySynStreamControlFrame;
 using spdy::kControlFlagMask;
+using spdy::kLengthMask;
 using spdy::CONTROL_FLAG_NONE;
 using spdy::DATA_FLAG_COMPRESSED;
 using spdy::DATA_FLAG_FIN;
 using spdy::SYN_STREAM;
 using spdy::test::CompareCharArraysWithHexError;
-using spdy::test::FramerSetEnableCompressionHelper;
 using spdy::test::TestSpdyVisitor;
 
 namespace spdy {
@@ -227,6 +302,22 @@
   }
 };
 
+
+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()));
+}
+
 // Test that we can encode and decode a SpdyHeaderBlock.
 TEST_F(SpdyFramerTest, HeaderBlock) {
   SpdyHeaderBlock headers;
@@ -247,14 +338,58 @@
   EXPECT_EQ(headers["gamma"], new_headers["gamma"]);
 }
 
+// Test that we can encode and decode a SpdyHeaderBlock in serialized form.
+TEST_F(SpdyFramerTest, HeaderBlockInBuffer) {
+  SpdyHeaderBlock headers;
+  headers["alpha"] = "beta";
+  headers["gamma"] = "charlie";
+  SpdyFramer framer;
+
+  // Encode the header block into a SynStream frame.
+  scoped_ptr<SpdySynStreamControlFrame> frame(
+      framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, false, &headers));
+  EXPECT_TRUE(frame.get() != NULL);
+  std::string serialized_headers(frame->header_block(),
+                                 frame->header_block_len());
+  SpdyHeaderBlock new_headers;
+  EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.c_str(),
+                                              serialized_headers.size(),
+                                              &new_headers));
+
+  EXPECT_EQ(headers.size(), new_headers.size());
+  EXPECT_EQ(headers["alpha"], new_headers["alpha"]);
+  EXPECT_EQ(headers["gamma"], new_headers["gamma"]);
+}
+
+// Test that if there's not a full frame, we fail to parse it.
+TEST_F(SpdyFramerTest, UndersizedHeaderBlockInBuffer) {
+  SpdyHeaderBlock headers;
+  headers["alpha"] = "beta";
+  headers["gamma"] = "charlie";
+  SpdyFramer framer;
+
+  // Encode the header block into a SynStream frame.
+  scoped_ptr<SpdySynStreamControlFrame> frame(
+      framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, false, &headers));
+  EXPECT_TRUE(frame.get() != NULL);
+
+  std::string serialized_headers(frame->header_block(),
+                                 frame->header_block_len());
+  SpdyHeaderBlock new_headers;
+  EXPECT_FALSE(framer.ParseHeaderBlockInBuffer(serialized_headers.c_str(),
+                                               serialized_headers.size() - 2,
+                                               &new_headers));
+}
+
 TEST_F(SpdyFramerTest, OutOfOrderHeaders) {
-  SpdyFrameBuilder frame;
+  // Frame builder with plentiful buffer size.
+  SpdyFrameBuilder frame(1024);
 
   frame.WriteUInt16(kControlFlagMask | 1);
   frame.WriteUInt16(SYN_STREAM);
   frame.WriteUInt32(0);  // Placeholder for the length.
   frame.WriteUInt32(3);  // stream_id
-  frame.WriteUInt32(0);  // associated stream id
+  frame.WriteUInt32(0);  // Associated stream id
   frame.WriteUInt16(0);  // Priority.
 
   frame.WriteUInt16(2);  // Number of headers.
@@ -268,8 +403,11 @@
 
   SpdyHeaderBlock new_headers;
   scoped_ptr<SpdyFrame> control_frame(frame.take());
+  SpdySynStreamControlFrame syn_frame(control_frame->data(), false);
+  std::string serialized_headers(syn_frame.header_block(),
+                                 syn_frame.header_block_len());
   SpdyFramer framer;
-  FramerSetEnableCompressionHelper(&framer, false);
+  framer.set_enable_compression(false);
   EXPECT_TRUE(framer.ParseHeaderBlock(control_frame.get(), &new_headers));
 }
 
@@ -311,13 +449,14 @@
   scoped_ptr<SpdyFrame> syn_frame1(frame1.take());
   scoped_ptr<SpdyFrame> syn_frame2(frame2.take());
   SpdyFramer framer;
-  FramerSetEnableCompressionHelper(&framer, false);
+  framer.set_enable_compression(false);
   EXPECT_FALSE(framer.ParseHeaderBlock(syn_frame1.get(), &new_headers));
   EXPECT_FALSE(framer.ParseHeaderBlock(syn_frame2.get(), &new_headers));
 }
 
 TEST_F(SpdyFramerTest, DuplicateHeader) {
-  SpdyFrameBuilder frame;
+  // Frame builder with plentiful buffer size.
+  SpdyFrameBuilder frame(1024);
 
   frame.WriteUInt16(kControlFlagMask | 1);
   frame.WriteUInt16(SYN_STREAM);
@@ -337,14 +476,18 @@
 
   SpdyHeaderBlock new_headers;
   scoped_ptr<SpdyFrame> control_frame(frame.take());
+  SpdySynStreamControlFrame syn_frame(control_frame->data(), false);
+  std::string serialized_headers(syn_frame.header_block(),
+                                 syn_frame.header_block_len());
   SpdyFramer framer;
-  FramerSetEnableCompressionHelper(&framer, false);
+  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));
 }
 
 TEST_F(SpdyFramerTest, MultiValueHeader) {
-  SpdyFrameBuilder frame;
+  // Frame builder with plentiful buffer size.
+  SpdyFrameBuilder frame(1024);
 
   frame.WriteUInt16(kControlFlagMask | 1);
   frame.WriteUInt16(SYN_STREAM);
@@ -363,8 +506,11 @@
 
   SpdyHeaderBlock new_headers;
   scoped_ptr<SpdyFrame> control_frame(frame.take());
+  SpdySynStreamControlFrame syn_frame(control_frame->data(), false);
+  std::string serialized_headers(syn_frame.header_block(),
+                                 syn_frame.header_block_len());
   SpdyFramer framer;
-  FramerSetEnableCompressionHelper(&framer, false);
+  framer.set_enable_compression(false);
   EXPECT_TRUE(framer.ParseHeaderBlock(control_frame.get(), &new_headers));
   EXPECT_TRUE(new_headers.find("name") != new_headers.end());
   EXPECT_EQ(value, new_headers.find("name")->second);
@@ -408,7 +554,7 @@
   headers["content-length"] = "12";
 
   SpdyFramer framer;
-  FramerSetEnableCompressionHelper(&framer, true);
+  framer.set_enable_compression(true);
   scoped_ptr<SpdySynStreamControlFrame>
       frame1(framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, true,
                                     &headers));
@@ -429,6 +575,16 @@
   EXPECT_EQ(0,
       memcmp(frame3->data(), frame4->data(),
       SpdyFrame::size() + frame3->length()));
+
+  // Expect frames 3 to be the same as a uncompressed frame created
+  // from scratch.
+  scoped_ptr<SpdySynStreamControlFrame>
+      uncompressed_frame(framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE,
+                                                false, &headers));
+  EXPECT_EQ(frame3->length(), uncompressed_frame->length());
+  EXPECT_EQ(0,
+      memcmp(frame3->data(), uncompressed_frame->data(),
+      SpdyFrame::size() + uncompressed_frame->length()));
 }
 
 TEST_F(SpdyFramerTest, DecompressUncompressedFrame) {
@@ -441,7 +597,7 @@
   headers["content-length"] = "12";
 
   SpdyFramer framer;
-  FramerSetEnableCompressionHelper(&framer, true);
+  framer.set_enable_compression(true);
   scoped_ptr<SpdySynStreamControlFrame>
       frame1(framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, false,
                                     &headers));
@@ -517,6 +673,7 @@
   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_);
 }
 
 // Test that the FIN flag on a data frame signifies EOF.
@@ -539,13 +696,13 @@
 
     0x00, 0x00, 0x00, 0x01,   // DATA on Stream #1
     0x00, 0x00, 0x00, 0x0c,
-      0xde, 0xad, 0xbe, 0xef,
-      0xde, 0xad, 0xbe, 0xef,
-      0xde, 0xad, 0xbe, 0xef,
+    0xde, 0xad, 0xbe, 0xef,
+    0xde, 0xad, 0xbe, 0xef,
+    0xde, 0xad, 0xbe, 0xef,
 
     0x00, 0x00, 0x00, 0x01,   // DATA on Stream #1, with EOF
     0x01, 0x00, 0x00, 0x04,
-      0xde, 0xad, 0xbe, 0xef,
+    0xde, 0xad, 0xbe, 0xef,
   };
 
   TestSpdyVisitor visitor;
@@ -559,6 +716,7 @@
   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_);
 }
 
 // Test that the FIN flag on a SYN reply frame signifies EOF.
@@ -573,8 +731,9 @@
     0x00, 0x02, 'v', 'v',
 
     0x80, 0x02, 0x00, 0x02,   // SYN REPLY Stream #1
-    0x01, 0x00, 0x00, 0x10,
+    0x01, 0x00, 0x00, 0x14,
     0x00, 0x00, 0x00, 0x01,
+    0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x01,
     0x00, 0x02, 'a', 'a',
     0x00, 0x02, 'b', 'b',
@@ -591,6 +750,7 @@
   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_);
 }
 
 // Basic compression & decompression
@@ -598,8 +758,8 @@
   SpdyFramer send_framer;
   SpdyFramer recv_framer;
 
-  FramerSetEnableCompressionHelper(&send_framer, true);
-  FramerSetEnableCompressionHelper(&recv_framer, true);
+  send_framer.set_enable_compression(true);
+  recv_framer.set_enable_compression(true);
 
   // Mix up some SYNs and DATA frames since they use different compressors.
   const char kHeader1[] = "header1";
@@ -694,7 +854,7 @@
 TEST_F(SpdyFramerTest, UnclosedStreamDataCompressors) {
   SpdyFramer send_framer;
 
-  FramerSetEnableCompressionHelper(&send_framer, false);
+  send_framer.set_enable_compression(true);
 
   const char kHeader1[] = "header1";
   const char kHeader2[] = "header2";
@@ -711,14 +871,14 @@
 
   const char bytes[] = "this is a test test test test test!";
   scoped_ptr<SpdyFrame> send_frame(
-      send_framer.CreateDataFrame(1,
-                                  bytes,
-                                  arraysize(bytes),
-                                  DATA_FLAG_FIN));
+      send_framer.CreateDataFrame(
+          1, bytes, arraysize(bytes),
+          DATA_FLAG_FIN));
   EXPECT_TRUE(send_frame.get() != NULL);
 
   // Run the inputs through the framer.
   TestSpdyVisitor visitor;
+  visitor.use_compression_ = true;
   const unsigned char* data;
   data = reinterpret_cast<const unsigned char*>(syn_frame->data());
   visitor.SimulateInFramer(data, syn_frame->length() + SpdyFrame::size());
@@ -733,6 +893,7 @@
   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_);
 
   // We closed the streams, so all compressors should be down.
   EXPECT_EQ(0, visitor.framer_.num_stream_compressors());
@@ -741,6 +902,22 @@
   EXPECT_EQ(0, send_framer.num_stream_decompressors());
 }
 
+TEST_F(SpdyFramerTest, WindowUpdateFrame) {
+  scoped_ptr<SpdyWindowUpdateControlFrame> window_update_frame(
+      SpdyFramer::CreateWindowUpdate(1, 0x12345678));
+
+  const unsigned char expected_data_frame[] = {
+      0x80, 0x02, 0x00, 0x09,
+      0x00, 0x00, 0x00, 0x08,
+      0x00, 0x00, 0x00, 0x01,
+      0x12, 0x34, 0x56, 0x78
+  };
+
+  EXPECT_EQ(16u, window_update_frame->size());
+  EXPECT_EQ(0,
+            memcmp(window_update_frame->data(), expected_data_frame, 16));
+}
+
 TEST_F(SpdyFramerTest, CreateDataFrame) {
   SpdyFramer framer;
 
@@ -805,11 +982,31 @@
         0x7fffffff, "hello", 5, DATA_FLAG_FIN));
     CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
   }
+
+  {
+    const char kDescription[] = "Large data frame";
+    const int kDataSize = 4 * 1024 * 1024;  // 4 MB
+    const std::string kData(kDataSize, 'A');
+    const unsigned char kFrameHeader[] = {
+      0x00, 0x00, 0x00, 0x01,
+      0x01, 0x40, 0x00, 0x00,
+    };
+
+    const int kFrameSize = arraysize(kFrameHeader) + kDataSize;
+    scoped_array<unsigned char> expected_frame_data(
+        new unsigned char[kFrameSize]);
+    memcpy(expected_frame_data.get(), kFrameHeader, arraysize(kFrameHeader));
+    memset(expected_frame_data.get() + arraysize(kFrameHeader), 'A', kDataSize);
+
+    scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame(
+        1, kData.data(), kData.size(), DATA_FLAG_FIN));
+    CompareFrame(kDescription, *frame, expected_frame_data.get(), kFrameSize);
+  }
 }
 
 TEST_F(SpdyFramerTest, CreateSynStreamUncompressed) {
   SpdyFramer framer;
-  FramerSetEnableCompressionHelper(&framer, false);
+  framer.set_enable_compression(false);
 
   {
     const char kDescription[] = "SYN_STREAM frame, lowest pri, no FIN";
@@ -834,6 +1031,8 @@
         1, 0, SPDY_PRIORITY_LOWEST, CONTROL_FLAG_NONE,
         false, &headers));
     CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+    EXPECT_EQ(1u, SpdyFramer::GetControlFrameStreamId(
+        reinterpret_cast<const SpdyControlFrame*>(frame.get())));
   }
 
   {
@@ -893,7 +1092,7 @@
 
 TEST_F(SpdyFramerTest, CreateSynStreamCompressed) {
   SpdyFramer framer;
-  FramerSetEnableCompressionHelper(&framer, true);
+  framer.set_enable_compression(true);
 
   {
     const char kDescription[] =
@@ -926,7 +1125,7 @@
 
 TEST_F(SpdyFramerTest, CreateSynReplyUncompressed) {
   SpdyFramer framer;
-  FramerSetEnableCompressionHelper(&framer, false);
+  framer.set_enable_compression(false);
 
   {
     const char kDescription[] = "SYN_REPLY frame, no FIN";
@@ -1002,7 +1201,7 @@
 
 TEST_F(SpdyFramerTest, CreateSynReplyCompressed) {
   SpdyFramer framer;
-  FramerSetEnableCompressionHelper(&framer, true);
+  framer.set_enable_compression(true);
 
   {
     const char kDescription[] = "SYN_REPLY frame, no FIN";
@@ -1043,6 +1242,8 @@
     };
     scoped_ptr<SpdyFrame> frame(framer.CreateRstStream(1, PROTOCOL_ERROR));
     CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+    EXPECT_EQ(1u, SpdyFramer::GetControlFrameStreamId(
+        reinterpret_cast<const SpdyControlFrame*>(frame.get())));
   }
 
   {
@@ -1114,6 +1315,9 @@
     };
     scoped_ptr<SpdyFrame> frame(framer.CreateSettings(settings));
     CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+    EXPECT_EQ(SpdyFramer::kInvalidStream,
+              SpdyFramer::GetControlFrameStreamId(
+                  reinterpret_cast<const SpdyControlFrame*>(frame.get())));
   }
 
   {
@@ -1142,6 +1346,27 @@
     };
     scoped_ptr<SpdyFrame> frame(framer.CreateNopFrame());
     CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+    EXPECT_EQ(SpdyFramer::kInvalidStream,
+              SpdyFramer::GetControlFrameStreamId(
+                  reinterpret_cast<const SpdyControlFrame*>(frame.get())));
+  }
+}
+
+TEST_F(SpdyFramerTest, CreatePingFrame) {
+  SpdyFramer framer;
+
+  {
+    const char kDescription[] = "PING frame";
+    const unsigned char kFrameData[] = {
+        0x80, 0x02, 0x00, 0x06,
+        0x00, 0x00, 0x00, 0x04,
+        0x12, 0x34, 0x56, 0x78,
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreatePingFrame(0x12345678u));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+    EXPECT_EQ(SpdyFramer::kInvalidStream,
+              SpdyFramer::GetControlFrameStreamId(
+                  reinterpret_cast<const SpdyControlFrame*>(frame.get())));
   }
 }
 
@@ -1157,6 +1382,9 @@
     };
     scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(0));
     CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+    EXPECT_EQ(SpdyFramer::kInvalidStream,
+              SpdyFramer::GetControlFrameStreamId(
+                  reinterpret_cast<const SpdyControlFrame*>(frame.get())));
   }
 
   {
@@ -1173,7 +1401,7 @@
 
 TEST_F(SpdyFramerTest, CreateHeadersUncompressed) {
   SpdyFramer framer;
-  FramerSetEnableCompressionHelper(&framer, false);
+  framer.set_enable_compression(false);
 
   {
     const char kDescription[] = "HEADERS frame, no FIN";
@@ -1249,7 +1477,7 @@
 
 TEST_F(SpdyFramerTest, CreateHeadersCompressed) {
   SpdyFramer framer;
-  FramerSetEnableCompressionHelper(&framer, true);
+  framer.set_enable_compression(true);
 
   {
     const char kDescription[] = "HEADERS frame, no FIN";
@@ -1290,6 +1518,8 @@
     };
     scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(1, 1));
     CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+    EXPECT_EQ(1u, SpdyFramer::GetControlFrameStreamId(
+        reinterpret_cast<const SpdyControlFrame*>(frame.get())));
   }
 
   {
@@ -1317,6 +1547,24 @@
   }
 }
 
+TEST_F(SpdyFramerTest, DuplicateFrame) {
+  SpdyFramer framer;
+
+  {
+    const char kDescription[] = "PING frame";
+    const unsigned char kFrameData[] = {
+        0x80, 0x02, 0x00, 0x06,
+        0x00, 0x00, 0x00, 0x04,
+        0x12, 0x34, 0x56, 0x78,
+    };
+    scoped_ptr<SpdyFrame> frame1(framer.CreatePingFrame(0x12345678u));
+    CompareFrame(kDescription, *frame1, kFrameData, arraysize(kFrameData));
+
+    scoped_ptr<SpdyFrame> frame2(framer.DuplicateFrame(*frame1));
+    CompareFrame(kDescription, *frame2, kFrameData, arraysize(kFrameData));
+  }
+}
+
 // This test case reproduces conditions that caused ExpandControlFrameBuffer to
 // fail to expand the buffer control frame buffer when it should have, allowing
 // the framer to overrun the buffer, and smash other heap contents. This test
@@ -1350,6 +1598,157 @@
   }
 }
 
+TEST_F(SpdyFramerTest, ReadGarbage) {
+  SpdyFramer framer;
+  unsigned char garbage_frame[256];
+  memset(garbage_frame, ~0, sizeof(garbage_frame));
+  TestSpdyVisitor visitor;
+  visitor.use_compression_ = false;
+  visitor.SimulateInFramer(garbage_frame, sizeof(garbage_frame));
+  EXPECT_EQ(1, visitor.error_count_);
+}
+
+TEST_F(SpdyFramerTest, ReadGarbageWithValidVersion) {
+  SpdyFramer framer;
+  char garbage_frame[256];
+  memset(garbage_frame, ~0, sizeof(garbage_frame));
+  SpdyControlFrame control_frame(&garbage_frame[0], false);
+  control_frame.set_version(kSpdyProtocolVersion);
+  TestSpdyVisitor visitor;
+  visitor.use_compression_ = false;
+  visitor.SimulateInFramer(
+      reinterpret_cast<unsigned char*>(control_frame.data()),
+      sizeof(garbage_frame));
+  EXPECT_EQ(1, visitor.error_count_);
+}
+
+TEST(SpdyFramer, StateToStringTest) {
+  EXPECT_STREQ("ERROR",
+               SpdyFramer::StateToString(SpdyFramer::SPDY_ERROR));
+  EXPECT_STREQ("DONE",
+               SpdyFramer::StateToString(SpdyFramer::SPDY_DONE));
+  EXPECT_STREQ("AUTO_RESET",
+               SpdyFramer::StateToString(SpdyFramer::SPDY_AUTO_RESET));
+  EXPECT_STREQ("RESET",
+               SpdyFramer::StateToString(SpdyFramer::SPDY_RESET));
+  EXPECT_STREQ("READING_COMMON_HEADER",
+               SpdyFramer::StateToString(
+                   SpdyFramer::SPDY_READING_COMMON_HEADER));
+  EXPECT_STREQ("INTERPRET_CONTROL_FRAME_COMMON_HEADER",
+               SpdyFramer::StateToString(
+                   SpdyFramer::SPDY_INTERPRET_CONTROL_FRAME_COMMON_HEADER));
+  EXPECT_STREQ("CONTROL_FRAME_PAYLOAD",
+               SpdyFramer::StateToString(
+                   SpdyFramer::SPDY_CONTROL_FRAME_PAYLOAD));
+  EXPECT_STREQ("IGNORE_REMAINING_PAYLOAD",
+               SpdyFramer::StateToString(
+                   SpdyFramer::SPDY_IGNORE_REMAINING_PAYLOAD));
+  EXPECT_STREQ("FORWARD_STREAM_FRAME",
+               SpdyFramer::StateToString(
+                   SpdyFramer::SPDY_FORWARD_STREAM_FRAME));
+  EXPECT_STREQ("SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK",
+               SpdyFramer::StateToString(
+                   SpdyFramer::SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK));
+  EXPECT_STREQ("SPDY_CONTROL_FRAME_HEADER_BLOCK",
+               SpdyFramer::StateToString(
+                   SpdyFramer::SPDY_CONTROL_FRAME_HEADER_BLOCK));
+  EXPECT_STREQ("UNKNOWN_STATE",
+               SpdyFramer::StateToString(
+                   SpdyFramer::SPDY_CONTROL_FRAME_HEADER_BLOCK + 1));
+}
+
+TEST(SpdyFramer, ErrorCodeToStringTest) {
+  EXPECT_STREQ("NO_ERROR",
+               SpdyFramer::ErrorCodeToString(SpdyFramer::SPDY_NO_ERROR));
+  EXPECT_STREQ("INVALID_CONTROL_FRAME",
+               SpdyFramer::ErrorCodeToString(
+                   SpdyFramer::SPDY_INVALID_CONTROL_FRAME));
+  EXPECT_STREQ("CONTROL_PAYLOAD_TOO_LARGE",
+               SpdyFramer::ErrorCodeToString(
+                   SpdyFramer::SPDY_CONTROL_PAYLOAD_TOO_LARGE));
+  EXPECT_STREQ("ZLIB_INIT_FAILURE",
+               SpdyFramer::ErrorCodeToString(
+                   SpdyFramer::SPDY_ZLIB_INIT_FAILURE));
+  EXPECT_STREQ("UNSUPPORTED_VERSION",
+               SpdyFramer::ErrorCodeToString(
+                   SpdyFramer::SPDY_UNSUPPORTED_VERSION));
+  EXPECT_STREQ("DECOMPRESS_FAILURE",
+               SpdyFramer::ErrorCodeToString(
+                   SpdyFramer::SPDY_DECOMPRESS_FAILURE));
+  EXPECT_STREQ("COMPRESS_FAILURE",
+               SpdyFramer::ErrorCodeToString(
+                   SpdyFramer::SPDY_COMPRESS_FAILURE));
+  EXPECT_STREQ("UNKNOWN_ERROR",
+               SpdyFramer::ErrorCodeToString(SpdyFramer::LAST_ERROR));
+}
+
+TEST(SpdyFramer, StatusCodeToStringTest) {
+  EXPECT_STREQ("INVALID",
+               SpdyFramer::StatusCodeToString(INVALID));
+  EXPECT_STREQ("PROTOCOL_ERROR",
+               SpdyFramer::StatusCodeToString(PROTOCOL_ERROR));
+  EXPECT_STREQ("INVALID_STREAM",
+               SpdyFramer::StatusCodeToString(INVALID_STREAM));
+  EXPECT_STREQ("REFUSED_STREAM",
+               SpdyFramer::StatusCodeToString(REFUSED_STREAM));
+  EXPECT_STREQ("UNSUPPORTED_VERSION",
+               SpdyFramer::StatusCodeToString(UNSUPPORTED_VERSION));
+  EXPECT_STREQ("CANCEL",
+               SpdyFramer::StatusCodeToString(CANCEL));
+  EXPECT_STREQ("INTERNAL_ERROR",
+               SpdyFramer::StatusCodeToString(INTERNAL_ERROR));
+  EXPECT_STREQ("FLOW_CONTROL_ERROR",
+               SpdyFramer::StatusCodeToString(FLOW_CONTROL_ERROR));
+  EXPECT_STREQ("UNKNOWN_STATUS",
+               SpdyFramer::StatusCodeToString(NUM_STATUS_CODES));
+}
+
+TEST(SpdyFramer, ControlTypeToStringTest) {
+  EXPECT_STREQ("SYN_STREAM",
+               SpdyFramer::ControlTypeToString(SYN_STREAM));
+  EXPECT_STREQ("SYN_REPLY",
+               SpdyFramer::ControlTypeToString(SYN_REPLY));
+  EXPECT_STREQ("RST_STREAM",
+               SpdyFramer::ControlTypeToString(RST_STREAM));
+  EXPECT_STREQ("SETTINGS",
+               SpdyFramer::ControlTypeToString(SETTINGS));
+  EXPECT_STREQ("NOOP",
+               SpdyFramer::ControlTypeToString(NOOP));
+  EXPECT_STREQ("PING",
+               SpdyFramer::ControlTypeToString(PING));
+  EXPECT_STREQ("GOAWAY",
+               SpdyFramer::ControlTypeToString(GOAWAY));
+  EXPECT_STREQ("HEADERS",
+               SpdyFramer::ControlTypeToString(HEADERS));
+  EXPECT_STREQ("WINDOW_UPDATE",
+               SpdyFramer::ControlTypeToString(WINDOW_UPDATE));
+  EXPECT_STREQ("UNKNOWN_CONTROL_TYPE",
+               SpdyFramer::ControlTypeToString(NUM_CONTROL_FRAME_TYPES));
+}
+
+TEST(SpdyFramer, GetMinimumControlFrameSizeTest) {
+  EXPECT_EQ(SpdySynStreamControlFrame::size(),
+            SpdyFramer::GetMinimumControlFrameSize(SYN_STREAM));
+  EXPECT_EQ(SpdySynReplyControlFrame::size(),
+            SpdyFramer::GetMinimumControlFrameSize(SYN_REPLY));
+  EXPECT_EQ(SpdyRstStreamControlFrame::size(),
+            SpdyFramer::GetMinimumControlFrameSize(RST_STREAM));
+  EXPECT_EQ(SpdySettingsControlFrame::size(),
+            SpdyFramer::GetMinimumControlFrameSize(SETTINGS));
+  EXPECT_EQ(SpdyNoOpControlFrame::size(),
+            SpdyFramer::GetMinimumControlFrameSize(NOOP));
+  EXPECT_EQ(SpdyPingControlFrame::size(),
+            SpdyFramer::GetMinimumControlFrameSize(PING));
+  EXPECT_EQ(SpdyGoAwayControlFrame::size(),
+            SpdyFramer::GetMinimumControlFrameSize(GOAWAY));
+  EXPECT_EQ(SpdyHeadersControlFrame::size(),
+            SpdyFramer::GetMinimumControlFrameSize(HEADERS));
+  EXPECT_EQ(SpdyWindowUpdateControlFrame::size(),
+            SpdyFramer::GetMinimumControlFrameSize(WINDOW_UPDATE));
+  EXPECT_EQ(static_cast<size_t>(0x7FFFFFFF),
+            SpdyFramer::GetMinimumControlFrameSize(NUM_CONTROL_FRAME_TYPES));
+}
+
 std::string RandomString(int length) {
   std::string rv;
   for (int index = 0; index < length; index++)
diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h
index ac6cd6b..eed9798 100644
--- a/net/spdy/spdy_protocol.h
+++ b/net/spdy/spdy_protocol.h
@@ -123,6 +123,7 @@
 //  +----------------------------------+
 //  |   Delta-Window-Size (32 bits)    |
 //  +----------------------------------+
+//
 namespace spdy {
 
 // This implementation of Spdy is version 2; It's like version 1, with some
@@ -166,6 +167,7 @@
 enum SpdyDataFlags {
   DATA_FLAG_NONE = 0,
   DATA_FLAG_FIN = 1,
+  // TODO(hkhalil): Remove.
   DATA_FLAG_COMPRESSED = 2
 };
 
diff --git a/net/spdy/spdy_protocol_test.cc b/net/spdy/spdy_protocol_test.cc
index dbc00dbd..7c575af 100644
--- a/net/spdy/spdy_protocol_test.cc
+++ b/net/spdy/spdy_protocol_test.cc
@@ -14,6 +14,7 @@
 using spdy::GOAWAY;
 using spdy::HEADERS;
 using spdy::NOOP;
+using spdy::NUM_CONTROL_FRAME_TYPES;
 using spdy::PING;
 using spdy::RST_STREAM;
 using spdy::SETTINGS;
@@ -27,7 +28,10 @@
 using spdy::SpdyFrame;
 using spdy::SpdyFramer;
 using spdy::SpdyHeaderBlock;
+using spdy::SpdyHeadersControlFrame;
 using spdy::SpdyGoAwayControlFrame;
+using spdy::SpdyNoOpControlFrame;
+using spdy::SpdyPingControlFrame;
 using spdy::SpdyRstStreamControlFrame;
 using spdy::SpdySettings;
 using spdy::SpdySettingsControlFrame;
@@ -50,8 +54,11 @@
   EXPECT_EQ(18u, SpdySynStreamControlFrame::size());
   EXPECT_EQ(14u, SpdySynReplyControlFrame::size());
   EXPECT_EQ(16u, SpdyRstStreamControlFrame::size());
-  EXPECT_EQ(12u, SpdyGoAwayControlFrame::size());
   EXPECT_EQ(12u, SpdySettingsControlFrame::size());
+  EXPECT_EQ(8u, SpdyNoOpControlFrame::size());
+  EXPECT_EQ(12u, SpdyPingControlFrame::size());
+  EXPECT_EQ(12u, SpdyGoAwayControlFrame::size());
+  EXPECT_EQ(14u, SpdyHeadersControlFrame::size());
   EXPECT_EQ(16u, SpdyWindowUpdateControlFrame::size());
   EXPECT_EQ(4u, sizeof(FlagsAndLength));
   EXPECT_EQ(1, SYN_STREAM);
@@ -125,6 +132,24 @@
   EXPECT_EQ(spdy::INVALID_STREAM, rst_frame->status());
   EXPECT_EQ(0, rst_frame->flags());
 
+  scoped_ptr<SpdyNoOpControlFrame> noop_frame(
+      framer.CreateNopFrame());
+  EXPECT_EQ(kSpdyProtocolVersion, noop_frame->version());
+  EXPECT_TRUE(noop_frame->is_control_frame());
+  EXPECT_EQ(NOOP, noop_frame->type());
+  EXPECT_EQ(0, noop_frame->flags());
+
+  const uint32 kUniqueId = 1234567u;
+  const uint32 kUniqueId2 = 31415926u;
+  scoped_ptr<SpdyPingControlFrame> ping_frame(
+      framer.CreatePingFrame(kUniqueId));
+  EXPECT_EQ(kSpdyProtocolVersion, ping_frame->version());
+  EXPECT_TRUE(ping_frame->is_control_frame());
+  EXPECT_EQ(PING, ping_frame->type());
+  EXPECT_EQ(kUniqueId, ping_frame->unique_id());
+  ping_frame->set_unique_id(kUniqueId2);
+  EXPECT_EQ(kUniqueId2, ping_frame->unique_id());
+
   scoped_ptr<SpdyGoAwayControlFrame> goaway_frame(
       framer.CreateGoAway(123));
   EXPECT_EQ(kSpdyProtocolVersion, goaway_frame->version());
@@ -132,6 +157,15 @@
   EXPECT_EQ(GOAWAY, goaway_frame->type());
   EXPECT_EQ(123u, goaway_frame->last_accepted_stream_id());
 
+  scoped_ptr<SpdyHeadersControlFrame> headers_frame(
+      framer.CreateHeaders(123, CONTROL_FLAG_NONE, false, &headers));
+  EXPECT_EQ(kSpdyProtocolVersion, headers_frame->version());
+  EXPECT_TRUE(headers_frame->is_control_frame());
+  EXPECT_EQ(HEADERS, headers_frame->type());
+  EXPECT_EQ(123u, headers_frame->stream_id());
+  EXPECT_EQ(2, headers_frame->header_block_len());
+  EXPECT_EQ(0, headers_frame->flags());
+
   scoped_ptr<SpdyWindowUpdateControlFrame> window_update_frame(
       framer.CreateWindowUpdate(123, 456));
   EXPECT_EQ(kSpdyProtocolVersion, window_update_frame->version());
@@ -230,6 +264,20 @@
   }
 }
 
+TEST(SpdyProtocolTest, HasHeaderBlock) {
+  SpdyControlFrame frame(SpdyControlFrame::size());
+  for (SpdyControlType type = SYN_STREAM;
+      type < NUM_CONTROL_FRAME_TYPES;
+      type = static_cast<SpdyControlType>(type + 1)) {
+    frame.set_type(type);
+    if (type == SYN_STREAM || type == SYN_REPLY || type == HEADERS) {
+      EXPECT_TRUE(frame.has_header_block());
+    } else {
+      EXPECT_FALSE(frame.has_header_block());
+    }
+  }
+}
+
 // Make sure that overflows both die in debug mode, and do not cause problems
 // in opt mode.  Note:  The EXPECT_DEBUG_DEATH call does not work on Win32 yet,
 // so we comment it out.
@@ -313,4 +361,22 @@
   }
 }
 
+TEST(SpdyProtocolDeathTest, TestRstStreamStatusBounds) {
+  SpdyFramer framer;
+  scoped_ptr<SpdyRstStreamControlFrame> rst_frame;
+
+  rst_frame.reset(framer.CreateRstStream(123, spdy::PROTOCOL_ERROR));
+  EXPECT_EQ(spdy::PROTOCOL_ERROR, rst_frame->status());
+
+  rst_frame->set_status(spdy::INVALID);
+  EXPECT_EQ(spdy::INVALID, rst_frame->status());
+
+  rst_frame->set_status(
+      static_cast<spdy::SpdyStatusCodes>(spdy::INVALID - 1));
+  EXPECT_EQ(spdy::INVALID, rst_frame->status());
+
+  rst_frame->set_status(spdy::NUM_STATUS_CODES);
+  EXPECT_EQ(spdy::INVALID, rst_frame->status());
+}
+
 }  // namespace