Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / net / spdy / spdy_framer_test.cc
index a7e4f38..8542835 100644 (file)
@@ -8,8 +8,10 @@
 
 #include "base/compiler_specific.h"
 #include "base/memory/scoped_ptr.h"
+#include "net/spdy/hpack_output_stream.h"
 #include "net/spdy/mock_spdy_framer_visitor.h"
 #include "net/spdy/spdy_frame_builder.h"
+#include "net/spdy/spdy_frame_reader.h"
 #include "net/spdy/spdy_framer.h"
 #include "net/spdy/spdy_protocol.h"
 #include "net/spdy/spdy_test_utils.h"
@@ -21,6 +23,8 @@ using std::string;
 using std::max;
 using std::min;
 using std::numeric_limits;
+using testing::ElementsAre;
+using testing::Pair;
 using testing::_;
 
 namespace net {
@@ -60,15 +64,9 @@ class SpdyFramerTestUtil {
     char* buffer = visitor.ReleaseBuffer();
     CHECK(buffer != NULL);
     SpdyFrame* decompressed_frame = new SpdyFrame(buffer, visitor.size(), true);
-    if (framer->protocol_version() == 4) {
-      SetFrameLength(decompressed_frame,
-                     visitor.size(),
-                     framer->protocol_version());
-    } else {
-      SetFrameLength(decompressed_frame,
-                     visitor.size() - framer->GetControlFrameHeaderSize(),
-                     framer->protocol_version());
-    }
+    SetFrameLength(decompressed_frame,
+                   visitor.size() - framer->GetControlFrameHeaderSize(),
+                   framer->protocol_version());
     return decompressed_frame;
   }
 
@@ -116,7 +114,6 @@ class SpdyFramerTestUtil {
     virtual void OnSynStream(SpdyStreamId stream_id,
                              SpdyStreamId associated_stream_id,
                              SpdyPriority priority,
-                             uint8 slot,
                              bool fin,
                              bool unidirectional) OVERRIDE {
       SpdyFramer framer(version_);
@@ -124,7 +121,6 @@ class SpdyFramerTestUtil {
       SpdySynStreamIR syn_stream(stream_id);
       syn_stream.set_associated_to_stream_id(associated_stream_id);
       syn_stream.set_priority(priority);
-      syn_stream.set_slot(slot);
       syn_stream.set_fin(fin);
       syn_stream.set_unidirectional(unidirectional);
       scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
@@ -153,15 +149,18 @@ class SpdyFramerTestUtil {
                            uint32 value) OVERRIDE {
       LOG(FATAL);
     }
-    virtual void OnPing(uint32 unique_id) OVERRIDE {
+    virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE {
       LOG(FATAL);
     }
+    virtual void OnSettingsEnd() OVERRIDE { LOG(FATAL); }
     virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
                           SpdyGoAwayStatus status) OVERRIDE {
       LOG(FATAL);
     }
 
-    virtual void OnHeaders(SpdyStreamId stream_id, bool fin) OVERRIDE {
+    virtual void OnHeaders(SpdyStreamId stream_id,
+                           bool fin,
+                           bool end) OVERRIDE {
       SpdyFramer framer(version_);
       framer.set_enable_compression(false);
       SpdyHeadersIR headers(stream_id);
@@ -177,7 +176,8 @@ class SpdyFramerTestUtil {
     }
 
     virtual void OnPushPromise(SpdyStreamId stream_id,
-                               SpdyStreamId promised_stream_id) OVERRIDE {
+                               SpdyStreamId promised_stream_id,
+                               bool end) OVERRIDE {
       SpdyFramer framer(version_);
       framer.set_enable_compression(false);
       SpdyPushPromiseIR push_promise(stream_id, promised_stream_id);
@@ -187,6 +187,10 @@ class SpdyFramerTestUtil {
       size_ += framer.GetPushPromiseMinimumSize();
     }
 
+    virtual void OnContinuation(SpdyStreamId stream_id, bool end) OVERRIDE {
+      LOG(FATAL);
+    }
+
     char* ReleaseBuffer() {
       CHECK(finished_);
       return buffer_.release();
@@ -218,6 +222,8 @@ class SpdyFramerTestUtil {
 class TestSpdyVisitor : public SpdyFramerVisitorInterface,
                         public SpdyFramerDebugVisitorInterface {
  public:
+  // This is larger than our max frame size because header blocks that
+  // are too long can spill over into CONTINUATION frames.
   static const size_t kDefaultHeaderBufferSize = 16 * 1024 * 1024;
 
   explicit TestSpdyVisitor(SpdyMajorVersion version)
@@ -227,8 +233,12 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
       syn_frame_count_(0),
       syn_reply_frame_count_(0),
       headers_frame_count_(0),
+      push_promise_frame_count_(0),
       goaway_count_(0),
       setting_count_(0),
+      settings_ack_sent_(0),
+      settings_ack_received_(0),
+      continuation_count_(0),
       last_window_update_stream_(0),
       last_window_update_delta_(0),
       last_push_promise_stream_(0),
@@ -254,13 +264,13 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
   virtual void OnError(SpdyFramer* f) OVERRIDE {
     LOG(INFO) << "SpdyFramer Error: "
               << SpdyFramer::ErrorCodeToString(f->error_code());
-    error_count_++;
+    ++error_count_;
   }
 
   virtual void OnDataFrameHeader(SpdyStreamId stream_id,
                                  size_t length,
                                  bool fin) OVERRIDE {
-    data_frame_count_++;
+    ++data_frame_count_;
     header_stream_id_ = stream_id;
   }
 
@@ -290,10 +300,13 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
     if (len == 0) {
       ++zero_length_control_frame_header_data_count_;
       // Indicates end-of-header-block.
+      headers_.clear();
       CHECK(header_buffer_valid_);
       size_t parsed_length = framer_.ParseHeaderBlockInBuffer(
           header_buffer_.get(), header_buffer_length_, &headers_);
-      DCHECK_EQ(header_buffer_length_, parsed_length);
+      LOG_IF(DFATAL, header_buffer_length_ != parsed_length)
+          << "Check failed: header_buffer_length_ == parsed_length "
+          << "(" << header_buffer_length_ << " vs. " << parsed_length << ")";
       return true;
     }
     const size_t available = header_buffer_size_ - header_buffer_length_;
@@ -309,27 +322,26 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
   virtual void OnSynStream(SpdyStreamId stream_id,
                            SpdyStreamId associated_stream_id,
                            SpdyPriority priority,
-                           uint8 credential_slot,
                            bool fin,
                            bool unidirectional) OVERRIDE {
-    syn_frame_count_++;
+    ++syn_frame_count_;
     InitHeaderStreaming(SYN_STREAM, stream_id);
     if (fin) {
-      fin_flag_count_++;
+      ++fin_flag_count_;
     }
   }
 
   virtual void OnSynReply(SpdyStreamId stream_id, bool fin) OVERRIDE {
-    syn_reply_frame_count_++;
+    ++syn_reply_frame_count_;
     InitHeaderStreaming(SYN_REPLY, stream_id);
     if (fin) {
-      fin_flag_count_++;
+      ++fin_flag_count_;
     }
   }
 
   virtual void OnRstStream(SpdyStreamId stream_id,
                            SpdyRstStreamStatus status) OVERRIDE {
-    fin_frame_count_++;
+    ++fin_frame_count_;
   }
 
   virtual bool OnRstStreamFrameData(const char* rst_stream_data,
@@ -343,23 +355,33 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
   virtual void OnSetting(SpdySettingsIds id,
                          uint8 flags,
                          uint32 value) OVERRIDE {
-    setting_count_++;
+    ++setting_count_;
+  }
+
+  virtual void OnSettingsAck() OVERRIDE {
+    DCHECK_LT(SPDY3, framer_.protocol_version());
+    ++settings_ack_received_;
   }
 
-  virtual void OnPing(uint32 unique_id) OVERRIDE {
+  virtual void OnSettingsEnd() OVERRIDE {
+    if (framer_.protocol_version() <= SPDY3) { return; }
+    ++settings_ack_sent_;
+  }
+
+  virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE {
     DLOG(FATAL);
   }
 
   virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
                         SpdyGoAwayStatus status) OVERRIDE {
-    goaway_count_++;
+    ++goaway_count_;
   }
 
-  virtual void OnHeaders(SpdyStreamId stream_id, bool fin) OVERRIDE {
-    headers_frame_count_++;
+  virtual void OnHeaders(SpdyStreamId stream_id, bool fin, bool end) OVERRIDE {
+    ++headers_frame_count_;
     InitHeaderStreaming(HEADERS, stream_id);
     if (fin) {
-      fin_flag_count_++;
+      ++fin_flag_count_;
     }
   }
 
@@ -370,12 +392,18 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
   }
 
   virtual void OnPushPromise(SpdyStreamId stream_id,
-                             SpdyStreamId promised_stream_id) OVERRIDE {
+                             SpdyStreamId promised_stream_id,
+                             bool end) OVERRIDE {
+    ++push_promise_frame_count_;
     InitHeaderStreaming(PUSH_PROMISE, stream_id);
     last_push_promise_stream_ = stream_id;
     last_push_promise_promised_stream_ = promised_stream_id;
   }
 
+  virtual void OnContinuation(SpdyStreamId stream_id, bool end) OVERRIDE {
+    ++continuation_count_;
+  }
+
   virtual void OnSendCompressedFrame(SpdyStreamId stream_id,
                                      SpdyFrameType type,
                                      size_t payload_len,
@@ -440,8 +468,12 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
   int syn_frame_count_;
   int syn_reply_frame_count_;
   int headers_frame_count_;
+  int push_promise_frame_count_;
   int goaway_count_;
   int setting_count_;
+  int settings_ack_sent_;
+  int settings_ack_received_;
+  int continuation_count_;
   SpdyStreamId last_window_update_stream_;
   uint32 last_window_update_delta_;
   SpdyStreamId last_push_promise_stream_;
@@ -468,12 +500,39 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
   SpdyHeaderBlock headers_;
 };
 
-// Retrieves serialized headers from SYN_STREAM frame.
-// Does not check that the given frame is a SYN_STREAM.
+// Retrieves serialized headers from a HEADERS or SYN_STREAM frame.
 base::StringPiece GetSerializedHeaders(const SpdyFrame* frame,
                                        const SpdyFramer& framer) {
-  return base::StringPiece(frame->data() + framer.GetSynStreamMinimumSize(),
-                           frame->size() - framer.GetSynStreamMinimumSize());
+  SpdyFrameReader reader(frame->data(), frame->size());
+  reader.Seek(2);  // Seek past the frame length.
+  SpdyFrameType frame_type;
+  if (framer.protocol_version() > SPDY3) {
+    uint8 serialized_type;
+    reader.ReadUInt8(&serialized_type);
+    frame_type = SpdyConstants::ParseFrameType(framer.protocol_version(),
+                                               serialized_type);
+    DCHECK_EQ(HEADERS, frame_type);
+    uint8 flags;
+    reader.ReadUInt8(&flags);
+    if (flags & HEADERS_FLAG_PRIORITY) {
+      frame_type = SYN_STREAM;
+    }
+  } else {
+    uint16 serialized_type;
+    reader.ReadUInt16(&serialized_type);
+    frame_type = SpdyConstants::ParseFrameType(framer.protocol_version(),
+                                               serialized_type);
+    DCHECK(frame_type == HEADERS ||
+           frame_type == SYN_STREAM) << frame_type;
+  }
+
+  if (frame_type == SYN_STREAM) {
+    return StringPiece(frame->data() + framer.GetSynStreamMinimumSize(),
+                       frame->size() - framer.GetSynStreamMinimumSize());
+  } else {
+    return StringPiece(frame->data() + framer.GetHeadersMinimumSize(),
+                       frame->size() - framer.GetHeadersMinimumSize());
+  }
 }
 
 }  // namespace test
@@ -493,7 +552,8 @@ class SpdyFramerTest : public ::testing::TestWithParam<SpdyMajorVersion> {
  protected:
   virtual void SetUp() {
     spdy_version_ = GetParam();
-    spdy_version_ch_ = static_cast<unsigned char>(spdy_version_);
+    spdy_version_ch_ = static_cast<unsigned char>(
+        SpdyConstants::SerializeMajorVersion(spdy_version_));
   }
 
   void CompareFrame(const string& description,
@@ -544,18 +604,6 @@ class SpdyFramerTest : public ::testing::TestWithParam<SpdyMajorVersion> {
     return true;
   }
 
-  void AddSpdySettingFromWireFormat(SettingsMap* settings,
-                                    uint32 key,
-                                    uint32 value) {
-    SettingsFlagsAndId flags_and_id =
-        SettingsFlagsAndId::FromWireFormat(spdy_version_, key);
-    SpdySettingsIds id = static_cast<SpdySettingsIds>(flags_and_id.id());
-    SpdySettingsFlags flags =
-        static_cast<SpdySettingsFlags>(flags_and_id.flags());
-    CHECK(settings->find(id) == settings->end());
-    settings->insert(std::make_pair(id, SettingsFlagsAndValue(flags, value)));
-  }
-
   bool IsSpdy2() { return spdy_version_ == SPDY2; }
   bool IsSpdy3() { return spdy_version_ == SPDY3; }
   bool IsSpdy4() { return spdy_version_ == SPDY4; }
@@ -580,19 +628,19 @@ TEST_P(SpdyFramerTest, HeaderBlockInBuffer) {
   syn_stream.set_priority(1);
   syn_stream.SetHeader("alpha", "beta");
   syn_stream.SetHeader("gamma", "charlie");
+  syn_stream.SetHeader("cookie", "key1=value1; key2=value2");
   scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
   EXPECT_TRUE(frame.get() != NULL);
-  base::StringPiece serialized_headers =
-      GetSerializedHeaders(frame.get(), framer);
-  SpdyHeaderBlock new_headers;
-  EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.data(),
-                                              serialized_headers.size(),
-                                              &new_headers));
 
-  SpdyHeaderBlock headers = syn_stream.name_value_block();
-  EXPECT_EQ(headers.size(), new_headers.size());
-  EXPECT_EQ(headers["alpha"], new_headers["alpha"]);
-  EXPECT_EQ(headers["gamma"], new_headers["gamma"]);
+  TestSpdyVisitor visitor(spdy_version_);
+  visitor.use_compression_ = false;
+  visitor.SimulateInFramer(
+      reinterpret_cast<unsigned char*>(frame->data()),
+      frame->size());
+
+  EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_);
+  EXPECT_TRUE(CompareHeaderBlocks(&syn_stream.name_value_block(),
+                                  &visitor.headers_));
 }
 
 // Test that if there's not a full frame, we fail to parse it.
@@ -608,74 +656,14 @@ TEST_P(SpdyFramerTest, UndersizedHeaderBlockInBuffer) {
   scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
   EXPECT_TRUE(frame.get() != NULL);
 
-  base::StringPiece serialized_headers =
-      GetSerializedHeaders(frame.get(), framer);
-  SpdyHeaderBlock new_headers;
-  EXPECT_FALSE(framer.ParseHeaderBlockInBuffer(serialized_headers.data(),
-                                               serialized_headers.size() - 2,
-                                               &new_headers));
-}
-
-TEST_P(SpdyFramerTest, OutOfOrderHeaders) {
-  SpdyFramer framer(spdy_version_);
-  framer.set_enable_compression(false);
-
-  // Frame builder with plentiful buffer size.
-  SpdyFrameBuilder frame(1024);
-  if (spdy_version_ < 4) {
-    frame.WriteControlFrameHeader(framer, SYN_STREAM, CONTROL_FLAG_NONE);
-    frame.WriteUInt32(3);  // stream_id
-  } else {
-    frame.WriteFramePrefix(framer, SYN_STREAM, CONTROL_FLAG_NONE, 3);
-  }
-
-  frame.WriteUInt32(0);  // Associated stream id
-  frame.WriteUInt16(0);  // Priority.
-
-  if (IsSpdy2()) {
-    frame.WriteUInt16(2);  // Number of headers.
-    frame.WriteString("gamma");
-    frame.WriteString("gamma");
-    frame.WriteString("alpha");
-    frame.WriteString("alpha");
-  } else {
-    frame.WriteUInt32(2);  // Number of headers.
-    frame.WriteStringPiece32("gamma");
-    frame.WriteStringPiece32("gamma");
-    frame.WriteStringPiece32("alpha");
-    frame.WriteStringPiece32("alpha");
-  }
-  // write the length
-  frame.RewriteLength(framer);
-
-  SpdyHeaderBlock new_headers;
-  scoped_ptr<SpdyFrame> control_frame(frame.take());
-  base::StringPiece serialized_headers =
-      GetSerializedHeaders(control_frame.get(), framer);
-  EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.data(),
-                                              serialized_headers.size(),
-                                              &new_headers));
-}
-
-// Test that if we receive a SYN_STREAM with stream ID zero, we signal an error
-// (but don't crash).
-TEST_P(SpdyFramerTest, SynStreamWithStreamIdZero) {
-  testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
-  SpdyFramer framer(spdy_version_);
-  framer.set_visitor(&visitor);
-
-  SpdySynStreamIR syn_stream(0);
-  syn_stream.set_priority(1);
-  syn_stream.SetHeader("alpha", "beta");
-  scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
-  ASSERT_TRUE(frame.get() != NULL);
+  TestSpdyVisitor visitor(spdy_version_);
+  visitor.use_compression_ = false;
+  visitor.SimulateInFramer(
+      reinterpret_cast<unsigned char*>(frame->data()),
+      frame->size() - 2);
 
-  // We shouldn't have to read the whole frame before we signal an error.
-  EXPECT_CALL(visitor, OnError(testing::Eq(&framer)));
-  EXPECT_GT(frame->size(), framer.ProcessInput(frame->data(), frame->size()));
-  EXPECT_TRUE(framer.HasError());
-  EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, framer.error_code())
-      << SpdyFramer::ErrorCodeToString(framer.error_code());
+  EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_);
+  EXPECT_EQ(0u, visitor.headers_.size());
 }
 
 // Test that if we receive a SYN_REPLY with stream ID zero, we signal an error
@@ -721,7 +709,7 @@ TEST_P(SpdyFramerTest, HeadersWithStreamIdZero) {
 // Test that if we receive a PUSH_PROMISE with stream ID zero, we signal an
 // error (but don't crash).
 TEST_P(SpdyFramerTest, PushPromiseWithStreamIdZero) {
-  if (spdy_version_ < SPDY4) {
+  if (spdy_version_ <= SPDY3) {
     return;
   }
 
@@ -746,7 +734,7 @@ TEST_P(SpdyFramerTest, PushPromiseWithStreamIdZero) {
 // Test that if we receive a PUSH_PROMISE with promised stream ID zero, we
 // signal an error (but don't crash).
 TEST_P(SpdyFramerTest, PushPromiseWithPromisedStreamIdZero) {
-  if (spdy_version_ < SPDY4) {
+  if (spdy_version_ <= SPDY3) {
     return;
   }
 
@@ -769,16 +757,22 @@ TEST_P(SpdyFramerTest, PushPromiseWithPromisedStreamIdZero) {
 }
 
 TEST_P(SpdyFramerTest, DuplicateHeader) {
+  if (spdy_version_ > SPDY3) {
+    // TODO(jgraettinger): Punting on this because we haven't determined
+    // whether duplicate HPACK headers other than Cookie are an error.
+    // If they are, this will need to be updated to use HpackOutputStream.
+    return;
+  }
   SpdyFramer framer(spdy_version_);
   // Frame builder with plentiful buffer size.
-  SpdyFrameBuilder frame(1024);
-  if (spdy_version_ < 4) {
+  SpdyFrameBuilder frame(1024, spdy_version_);
+  if (spdy_version_ <= SPDY3) {
     frame.WriteControlFrameHeader(framer, SYN_STREAM, CONTROL_FLAG_NONE);
     frame.WriteUInt32(3);  // stream_id
     frame.WriteUInt32(0);  // associated stream id
     frame.WriteUInt16(0);  // Priority.
   } else {
-    frame.WriteFramePrefix(framer, HEADERS, HEADERS_FLAG_PRIORITY, 3);
+    frame.BeginNewFrame(framer, HEADERS, HEADERS_FLAG_PRIORITY, 3);
     frame.WriteUInt32(framer.GetHighestPriority());
   }
 
@@ -812,22 +806,33 @@ TEST_P(SpdyFramerTest, DuplicateHeader) {
 TEST_P(SpdyFramerTest, MultiValueHeader) {
   SpdyFramer framer(spdy_version_);
   // Frame builder with plentiful buffer size.
-  SpdyFrameBuilder frame(1024);
-  if (spdy_version_ < 4) {
+  SpdyFrameBuilder frame(1024, spdy_version_);
+  if (spdy_version_ <= SPDY3) {
     frame.WriteControlFrameHeader(framer, SYN_STREAM, CONTROL_FLAG_NONE);
     frame.WriteUInt32(3);  // stream_id
     frame.WriteUInt32(0);  // associated stream id
     frame.WriteUInt16(0);  // Priority.
   } else {
-    frame.WriteFramePrefix(framer, HEADERS, HEADERS_FLAG_PRIORITY, 3);
+    frame.BeginNewFrame(framer,
+                        HEADERS,
+                        HEADERS_FLAG_PRIORITY | HEADERS_FLAG_END_HEADERS,
+                        3);
     frame.WriteUInt32(framer.GetHighestPriority());
   }
 
-  string value("value1\0value2");
+  string value("value1\0value2", 13);
   if (IsSpdy2()) {
     frame.WriteUInt16(1);  // Number of headers.
     frame.WriteString("name");
     frame.WriteString(value);
+  } else if (spdy_version_ > SPDY3) {
+    // TODO(jgraettinger): If this pattern appears again, move to test class.
+    std::map<string, string> header_set;
+    header_set["name"] = value;
+    string buffer;
+    HpackEncoder encoder(ObtainHpackHuffmanTable());
+    encoder.EncodeHeaderSetWithoutCompression(header_set, &buffer);
+    frame.WriteBytes(&buffer[0], buffer.size());
   } else {
     frame.WriteUInt32(1);  // Number of headers.
     frame.WriteStringPiece32("name");
@@ -836,19 +841,24 @@ TEST_P(SpdyFramerTest, MultiValueHeader) {
   // write the length
   frame.RewriteLength(framer);
 
-  SpdyHeaderBlock new_headers;
   framer.set_enable_compression(false);
   scoped_ptr<SpdyFrame> control_frame(frame.take());
-  base::StringPiece serialized_headers =
-      GetSerializedHeaders(control_frame.get(), framer);
-  EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.data(),
-                                              serialized_headers.size(),
-                                              &new_headers));
-  EXPECT_TRUE(new_headers.find("name") != new_headers.end());
-  EXPECT_EQ(value, new_headers.find("name")->second);
+
+  TestSpdyVisitor visitor(spdy_version_);
+  visitor.use_compression_ = false;
+  visitor.SimulateInFramer(
+      reinterpret_cast<unsigned char*>(control_frame->data()),
+      control_frame->size());
+
+  EXPECT_THAT(visitor.headers_, ElementsAre(
+      Pair("name", value)));
 }
 
 TEST_P(SpdyFramerTest, BasicCompression) {
+  if (spdy_version_ > SPDY3) {
+    // Deflate compression doesn't apply to HPACK.
+    return;
+  }
   scoped_ptr<TestSpdyVisitor> visitor(new TestSpdyVisitor(spdy_version_));
   SpdyFramer framer(spdy_version_);
   framer.set_debug_visitor(visitor.get());
@@ -990,7 +1000,7 @@ TEST_P(SpdyFramerTest, Basic) {
     0x80, spdy_version_ch_, 0x00, 0x03,  // RST_STREAM on Stream #1
     0x00, 0x00, 0x00, 0x08,
     0x00, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x05,           // RST_STREAM_CANCEL
 
     0x00, 0x00, 0x00, 0x03,           // DATA on Stream #3
     0x00, 0x00, 0x00, 0x00,
@@ -998,7 +1008,7 @@ TEST_P(SpdyFramerTest, Basic) {
     0x80, spdy_version_ch_, 0x00, 0x03,  // RST_STREAM on Stream #3
     0x00, 0x00, 0x00, 0x08,
     0x00, 0x00, 0x00, 0x03,
-    0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x05,           // RST_STREAM_CANCEL
   };
 
   const unsigned char kV3Input[] = {
@@ -1049,7 +1059,7 @@ TEST_P(SpdyFramerTest, Basic) {
     0x80, spdy_version_ch_, 0x00, 0x03,  // RST_STREAM on Stream #1
     0x00, 0x00, 0x00, 0x08,
     0x00, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x05,           // RST_STREAM_CANCEL
 
     0x00, 0x00, 0x00, 0x03,           // DATA on Stream #3
     0x00, 0x00, 0x00, 0x00,
@@ -1057,58 +1067,51 @@ TEST_P(SpdyFramerTest, Basic) {
     0x80, spdy_version_ch_, 0x00, 0x03,  // RST_STREAM on Stream #3
     0x00, 0x00, 0x00, 0x08,
     0x00, 0x00, 0x00, 0x03,
-    0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x05,           // RST_STREAM_CANCEL
   };
 
+  // SYN_STREAM doesn't exist in SPDY4, so instead we send
+  // HEADERS frames with PRIORITY and END_HEADERS set.
   const unsigned char kV4Input[] = {
-    0x00, 0x1c, 0x08, 0x08,           // SYN_STREAM #1
-    0x00, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, 0x02,
-    'h',  'h',  0x00, 0x00,
-    0x00, 0x02, 'v',  'v',
+    0x00, 0x05, 0x01, 0x0c,           // HEADERS: PRIORITY | END_HEADERS
+    0x00, 0x00, 0x00, 0x01,           // Stream 1
+    0x00, 0x00, 0x00, 0x00,           // Priority 0
+    0x82,                             // :method: GET
 
-    0x00, 0x24, 0x08, 0x00,           // HEADERS on Stream #1
-    0x00, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, 0x02,
-    0x00, 0x00, 0x00, 0x02,
-    'h',  '2',  0x00, 0x00,
-    0x00, 0x02, 'v',  '2',
-    0x00, 0x00, 0x00, 0x02,
-    'h',  '3',  0x00, 0x00,
-    0x00, 0x02, 'v', '3',
+    0x00, 0x01, 0x01, 0x04,           // HEADERS: END_HEADERS
+    0x00, 0x00, 0x00, 0x01,           // Stream 1
+    0x8c,                             // :status: 200
 
-    0x00, 0x14, 0x00, 0x00,           // DATA on Stream #1
+    0x00, 0x0c, 0x00, 0x00,           // DATA on Stream #1
     0x00, 0x00, 0x00, 0x01,
     0xde, 0xad, 0xbe, 0xef,
     0xde, 0xad, 0xbe, 0xef,
     0xde, 0xad, 0xbe, 0xef,
 
-    0x00, 0x10, 0x08, 0x08,           // SYN Stream #3
-    0x00, 0x00, 0x00, 0x03,
-    0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00,
+    0x00, 0x05, 0x01, 0x0c,           // HEADERS: PRIORITY | END_HEADERS
+    0x00, 0x00, 0x00, 0x03,           // Stream 3
+    0x00, 0x00, 0x00, 0x00,           // Priority 0
+    0x82,                             // :method: GET
 
-    0x00, 0x10, 0x00, 0x00,           // DATA on Stream #3
+    0x00, 0x08, 0x00, 0x00,           // DATA on Stream #3
     0x00, 0x00, 0x00, 0x03,
     0xde, 0xad, 0xbe, 0xef,
     0xde, 0xad, 0xbe, 0xef,
 
-    0x00, 0x0c, 0x00, 0x00,           // DATA on Stream #1
+    0x00, 0x04, 0x00, 0x00,           // DATA on Stream #1
     0x00, 0x00, 0x00, 0x01,
     0xde, 0xad, 0xbe, 0xef,
 
-    0x00, 0x0c, 0x03, 0x00,           // RST_STREAM on Stream #1
+    0x00, 0x04, 0x03, 0x00,           // RST_STREAM on Stream #1
     0x00, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x08,           // RST_STREAM_CANCEL
 
-    0x00, 0x08, 0x00, 0x00,           // DATA on Stream #3
+    0x00, 0x00, 0x00, 0x00,           // DATA on Stream #3
     0x00, 0x00, 0x00, 0x03,
 
-    0x00, 0x17, 0x03, 0x00,           // RST_STREAM on Stream #3
+    0x00, 0x0f, 0x03, 0x00,           // RST_STREAM on Stream #3
     0x00, 0x00, 0x00, 0x03,
-    0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x08,           // RST_STREAM_CANCEL
     0x52, 0x45, 0x53, 0x45,           // opaque data
     0x54, 0x53, 0x54, 0x52,
     0x45, 0x41, 0x4d,
@@ -1128,19 +1131,10 @@ TEST_P(SpdyFramerTest, Basic) {
   EXPECT_EQ(1, visitor.headers_frame_count_);
   EXPECT_EQ(24, visitor.data_bytes_);
 
-  // TODO(jgraettinger): We expect to handle but not emit RST payloads.
-  if (false /*!FLAGS_gfe2_restart_flag_allow_spdy_4_rst_stream_opaque_data*/ &&
-      IsSpdy4()) {
-    // Last rst_stream is an INVALID_CONTROL_FRAME.
-    EXPECT_EQ(1, visitor.error_count_);
-    EXPECT_EQ(1, visitor.fin_frame_count_);
-  } else {
-    EXPECT_EQ(0, visitor.error_count_);
-    EXPECT_EQ(2, visitor.fin_frame_count_);
-  }
+  EXPECT_EQ(0, visitor.error_count_);
+  EXPECT_EQ(2, visitor.fin_frame_count_);
 
-  if (true /*FLAGS_gfe2_restart_flag_allow_spdy_4_rst_stream_opaque_data*/ &&
-      IsSpdy4()) {
+  if (IsSpdy4()) {
     base::StringPiece reset_stream = "RESETSTREAM";
     EXPECT_EQ(reset_stream, visitor.fin_opaque_data_);
   } else {
@@ -1210,29 +1204,26 @@ TEST_P(SpdyFramerTest, FinOnDataFrame) {
     0x01, 0x00, 0x00, 0x04,
     0xde, 0xad, 0xbe, 0xef,
   };
+
+  // SYN_STREAM and SYN_REPLY don't exist in SPDY4, so instead we send
+  // HEADERS frames with PRIORITY(SYN_STREAM only) and END_HEADERS set.
   const unsigned char kV4Input[] = {
-    0x00, 0x1c, 0x08, 0x08,           // SYN_STREAM #1
-    0x00, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, 0x02,
-    'h',  'h',  0x00, 0x00,
-    0x00, 0x02, 'v',  'v',
+    0x00, 0x05, 0x01, 0x0c,           // HEADERS: PRIORITY | END_HEADERS
+    0x00, 0x00, 0x00, 0x01,           // Stream 1
+    0x00, 0x00, 0x00, 0x00,           // Priority 0
+    0x82,                             // :method: GET
 
-    0x00, 0x18, 0x08, 0x00,           // SYN REPLY Stream #1
-    0x00, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, 0x02,
-    'a',  'a',  0x00, 0x00,
-    0x00, 0x02, 'b', 'b',
+    0x00, 0x01, 0x01, 0x04,           // HEADERS: END_HEADERS
+    0x00, 0x00, 0x00, 0x01,           // Stream 1
+    0x8c,                             // :status: 200
 
-    0x00, 0x14, 0x00, 0x00,           // DATA on Stream #1
+    0x00, 0x0c, 0x00, 0x00,           // DATA on Stream #1
     0x00, 0x00, 0x00, 0x01,
     0xde, 0xad, 0xbe, 0xef,
     0xde, 0xad, 0xbe, 0xef,
     0xde, 0xad, 0xbe, 0xef,
 
-    0x00, 0x0c, 0x00, 0x01,           // DATA on Stream #1, with FIN
+    0x00, 0x04, 0x00, 0x01,           // DATA on Stream #1, with FIN
     0x00, 0x00, 0x00, 0x01,
     0xde, 0xad, 0xbe, 0xef,
   };
@@ -1299,21 +1290,18 @@ TEST_P(SpdyFramerTest, FinOnSynReplyFrame) {
     'a', 'a',   0x00, 0x00,
     0x00, 0x02, 'b', 'b',
   };
-  const unsigned char kV4Input[] = {
-    0x00, 0x1c, 0x08, 0x08,           // SYN_STREAM #1
-    0x00, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, 0x02,
-    'h',  'h',  0x00, 0x00,
-    0x00, 0x02, 'v',  'v',
 
-    0x00, 0x18, 0x08, 0x01,           // SYN_REPLY #1, with FIN
-    0x00, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, 0x02,
-    'a', 'a',   0x00, 0x00,
-    0x00, 0x02, 'b', 'b',
+  // SYN_STREAM and SYN_REPLY don't exist in SPDY4, so instead we send
+  // HEADERS frames with PRIORITY(SYN_STREAM only) and END_HEADERS set.
+  const unsigned char kV4Input[] = {
+    0x00, 0x05, 0x01, 0x0c,           // HEADERS: PRIORITY | END_HEADERS
+    0x00, 0x00, 0x00, 0x01,           // Stream 1
+    0x00, 0x00, 0x00, 0x00,           // Priority 0
+    0x82,                             // :method: GET
+
+    0x00, 0x01, 0x01, 0x05,           // HEADERS: FIN | END_HEADERS
+    0x00, 0x00, 0x00, 0x01,           // Stream 1
+    0x8c,                             // :status: 200
   };
 
   TestSpdyVisitor visitor(spdy_version_);
@@ -1342,6 +1330,10 @@ TEST_P(SpdyFramerTest, FinOnSynReplyFrame) {
 }
 
 TEST_P(SpdyFramerTest, HeaderCompression) {
+  if (spdy_version_ > SPDY3) {
+    // Deflate compression doesn't apply to HPACK.
+    return;
+  }
   SpdyFramer send_framer(spdy_version_);
   SpdyFramer recv_framer(spdy_version_);
 
@@ -1422,7 +1414,7 @@ TEST_P(SpdyFramerTest, UnclosedStreamDataCompressors) {
   EXPECT_TRUE(syn_frame.get() != NULL);
 
   StringPiece bytes = "this is a test test test test test!";
-  net::SpdyDataIR data_ir(1, bytes);
+  SpdyDataIR data_ir(1, bytes);
   data_ir.set_fin(true);
   scoped_ptr<SpdyFrame> send_frame(send_framer.SerializeData(data_ir));
   EXPECT_TRUE(send_frame.get() != NULL);
@@ -1466,7 +1458,7 @@ TEST_P(SpdyFramerTest, UnclosedStreamDataCompressorsOneByteAtATime) {
   EXPECT_TRUE(syn_frame.get() != NULL);
 
   const char bytes[] = "this is a test test test test test!";
-  net::SpdyDataIR data_ir(1, StringPiece(bytes, arraysize(bytes)));
+  SpdyDataIR data_ir(1, StringPiece(bytes, arraysize(bytes)));
   data_ir.set_fin(true);
   scoped_ptr<SpdyFrame> send_frame(send_framer.SerializeData(data_ir));
   EXPECT_TRUE(send_frame.get() != NULL);
@@ -1500,7 +1492,7 @@ TEST_P(SpdyFramerTest, UnclosedStreamDataCompressorsOneByteAtATime) {
 TEST_P(SpdyFramerTest, WindowUpdateFrame) {
   SpdyFramer framer(spdy_version_);
   scoped_ptr<SpdyFrame> frame(framer.SerializeWindowUpdate(
-      net::SpdyWindowUpdateIR(1, 0x12345678)));
+      SpdyWindowUpdateIR(1, 0x12345678)));
 
   const char kDescription[] = "WINDOW_UPDATE frame, stream 1, delta 0x12345678";
   const unsigned char kV3FrameData[] = {  // Also applies for V2.
@@ -1510,15 +1502,15 @@ TEST_P(SpdyFramerTest, WindowUpdateFrame) {
     0x12, 0x34, 0x56, 0x78
   };
   const unsigned char kV4FrameData[] = {
-    0x00, 0x0c, 0x09, 0x00,
+    0x00, 0x04, 0x08, 0x00,
     0x00, 0x00, 0x00, 0x01,
     0x12, 0x34, 0x56, 0x78
   };
 
   if (IsSpdy4()) {
-     CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+    CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
   } else {
-     CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
+    CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
   }
 }
 
@@ -1534,7 +1526,7 @@ TEST_P(SpdyFramerTest, CreateDataFrame) {
       'o'
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x0d, 0x00, 0x00,
+      0x00, 0x05, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x01,
       'h',  'e',  'l',  'l',
       'o'
@@ -1553,7 +1545,143 @@ TEST_P(SpdyFramerTest, CreateDataFrame) {
 
     SpdyDataIR data_header_ir(1);
     data_header_ir.SetDataShallow(base::StringPiece(bytes, strlen(bytes)));
-    frame.reset(framer.SerializeDataFrameHeader(data_header_ir));
+    frame.reset(framer.SerializeDataFrameHeaderWithPaddingLengthField(
+        data_header_ir));
+    CompareCharArraysWithHexError(
+        kDescription,
+        reinterpret_cast<const unsigned char*>(frame->data()),
+        framer.GetDataFrameMinimumSize(),
+        IsSpdy4() ? kV4FrameData : kV3FrameData,
+        framer.GetDataFrameMinimumSize());
+  }
+
+  {
+    const char kDescription[] = "'hello' data frame with more padding, no FIN";
+    const unsigned char kV3FrameData[] = {  // Also applies for V2.
+      0x00, 0x00, 0x00, 0x01,
+      0x00, 0x00, 0x00, 0x05,
+      'h',  'e',  'l',  'l',
+      'o'
+    };
+
+    const unsigned char kV4FrameData[] = {
+      0x01, 0x0b, 0x00, 0x30,      // Length = 267.  PAD_HIGH and PAD_LOW set.
+      0x00, 0x00, 0x00, 0x01,
+      0x01, 0x04,                  // Pad Low and Pad High fields.
+      'h',  'e',  'l',  'l',       // Data
+      'o',
+      // Padding of 260 zeros (so both PAD_HIGH and PAD_LOW fields are used).
+      '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', '0',  '0',  '0',  '0', '0',
+      '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', '0',  '0',  '0',  '0', '0',
+      '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', '0',  '0',  '0',  '0', '0',
+      '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', '0',  '0',  '0',  '0', '0',
+      '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', '0',  '0',  '0',  '0', '0',
+      '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', '0',  '0',  '0',  '0', '0',
+      '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', '0',  '0',  '0',  '0', '0',
+      '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', '0',  '0',  '0',  '0', '0',
+      '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', '0',  '0',  '0',  '0', '0',
+      '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', '0',  '0',  '0',  '0', '0',
+      '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', '0',  '0',  '0',  '0', '0',
+      '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', '0',  '0',  '0',  '0', '0',
+      '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', '0',  '0',  '0',  '0', '0',
+      '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', '0',  '0',  '0',  '0', '0',
+      '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', '0',  '0',  '0',  '0', '0',
+      '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', '0',  '0',  '0',  '0', '0',
+      '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', '0',  '0',  '0',  '0', '0',
+      '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', '0',  '0',  '0',  '0', '0',
+      '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', '0',  '0',  '0',  '0', '0',
+      '0',  '0',  '0',  '0',  '0',  '0',  '0',  '0', '0',  '0',  '0',  '0', '0',
+    };
+    const char bytes[] = "hello";
+
+    SpdyDataIR data_ir(1, StringPiece(bytes, strlen(bytes)));
+    // 260 zeros and the pad low/high fields make the overall padding to be 262
+    // bytes.
+    data_ir.set_padding_len(262);
+    scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
+    if (IsSpdy4()) {
+       CompareFrame(
+           kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+    } else {
+       CompareFrame(
+           kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
+    }
+
+    frame.reset(framer.SerializeDataFrameHeaderWithPaddingLengthField(data_ir));
+    CompareCharArraysWithHexError(
+        kDescription,
+        reinterpret_cast<const unsigned char*>(frame->data()),
+        framer.GetDataFrameMinimumSize(),
+        IsSpdy4() ? kV4FrameData : kV3FrameData,
+        framer.GetDataFrameMinimumSize());
+  }
+
+  {
+    const char kDescription[] = "'hello' data frame with few padding, no FIN";
+    const unsigned char kV3FrameData[] = {  // Also applies for V2.
+      0x00, 0x00, 0x00, 0x01,
+      0x00, 0x00, 0x00, 0x05,
+      'h',  'e',  'l',  'l',
+      'o'
+    };
+
+    const unsigned char kV4FrameData[] = {
+      0x00, 0x0d, 0x00, 0x10,      // Length = 13.  PAD_LOW set.
+      0x00, 0x00, 0x00, 0x01,
+      0x07,                        // Pad Low field.
+      'h',  'e',  'l',  'l',       // Data
+      'o',
+      '0',  '0',  '0',  '0',       // Padding
+      '0',  '0',  '0'
+    };
+    const char bytes[] = "hello";
+
+    SpdyDataIR data_ir(1, StringPiece(bytes, strlen(bytes)));
+    // 7 zeros and the pad low field make the overall padding to be 8 bytes.
+    data_ir.set_padding_len(8);
+    scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
+    if (IsSpdy4()) {
+       CompareFrame(
+           kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+    } else {
+       CompareFrame(
+           kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
+    }
+  }
+
+  {
+    const char kDescription[] =
+        "'hello' data frame with 1 byte padding, no FIN";
+    const unsigned char kV3FrameData[] = {  // Also applies for V2.
+      0x00, 0x00, 0x00, 0x01,
+      0x00, 0x00, 0x00, 0x05,
+      'h',  'e',  'l',  'l',
+      'o'
+    };
+
+    const unsigned char kV4FrameData[] = {
+      0x00, 0x06, 0x00, 0x10,      // Length = 6.  PAD_LOW set.
+      0x00, 0x00, 0x00, 0x01,
+      0x00,                        // Pad Low field.
+      'h',  'e',  'l',  'l',       // Data
+      'o',
+    };
+    const char bytes[] = "hello";
+
+    SpdyDataIR data_ir(1, StringPiece(bytes, strlen(bytes)));
+    // The pad low field itself is used for the 1-byte padding and no padding
+    // payload is needed.
+    data_ir.set_padding_len(1);
+    scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
+    if (IsSpdy4()) {
+       CompareFrame(
+           kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+    } else {
+       CompareFrame(
+           kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
+    }
+
+    frame.reset(framer.SerializeDataFrameHeaderWithPaddingLengthField(data_ir));
     CompareCharArraysWithHexError(
         kDescription,
         reinterpret_cast<const unsigned char*>(frame->data()),
@@ -1570,11 +1698,11 @@ TEST_P(SpdyFramerTest, CreateDataFrame) {
       0xff
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x09, 0x00, 0x00,
+      0x00, 0x01, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x01,
       0xff
     };
-    net::SpdyDataIR data_ir(1, StringPiece("\xff", 1));
+    SpdyDataIR data_ir(1, StringPiece("\xff", 1));
     scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
     if (IsSpdy4()) {
        CompareFrame(
@@ -1594,12 +1722,12 @@ TEST_P(SpdyFramerTest, CreateDataFrame) {
       'o'
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x0d, 0x00, 0x01,
+      0x00, 0x05, 0x00, 0x01,
       0x00, 0x00, 0x00, 0x01,
       'h',  'e',  'l',  'l',
       'o'
     };
-    net::SpdyDataIR data_ir(1, StringPiece("hello", 5));
+    SpdyDataIR data_ir(1, StringPiece("hello", 5));
     data_ir.set_fin(true);
     scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
     if (IsSpdy4()) {
@@ -1618,10 +1746,10 @@ TEST_P(SpdyFramerTest, CreateDataFrame) {
       0x00, 0x00, 0x00, 0x00,
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x08, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x01,
     };
-    net::SpdyDataIR data_ir(1, StringPiece());
+    SpdyDataIR data_ir(1, StringPiece());
     scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
     if (IsSpdy4()) {
        CompareFrame(
@@ -1630,6 +1758,14 @@ TEST_P(SpdyFramerTest, CreateDataFrame) {
        CompareFrame(
            kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
     }
+
+    frame.reset(framer.SerializeDataFrameHeaderWithPaddingLengthField(data_ir));
+    CompareCharArraysWithHexError(
+        kDescription,
+        reinterpret_cast<const unsigned char*>(frame->data()),
+        framer.GetDataFrameMinimumSize(),
+        IsSpdy4() ? kV4FrameData : kV3FrameData,
+        framer.GetDataFrameMinimumSize());
   }
 
   {
@@ -1641,12 +1777,12 @@ TEST_P(SpdyFramerTest, CreateDataFrame) {
       'o'
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x0d, 0x00, 0x01,
+      0x00, 0x05, 0x00, 0x01,
       0x7f, 0xff, 0xff, 0xff,
       'h',  'e',  'l',  'l',
       'o'
     };
-    net::SpdyDataIR data_ir(0x7fffffff, "hello");
+    SpdyDataIR data_ir(0x7fffffff, "hello");
     data_ir.set_fin(true);
     scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
     if (IsSpdy4()) {
@@ -1675,7 +1811,7 @@ TEST_P(SpdyFramerTest, CreateDataFrame) {
     memcpy(expected_frame_data.get(), kFrameHeader, arraysize(kFrameHeader));
     memset(expected_frame_data.get() + arraysize(kFrameHeader), 'A', kDataSize);
 
-    net::SpdyDataIR data_ir(1, StringPiece(kData.data(), kData.size()));
+    SpdyDataIR data_ir(1, StringPiece(kData.data(), kData.size()));
     data_ir.set_fin(true);
     scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
     CompareFrame(kDescription, *frame, expected_frame_data.get(), kFrameSize);
@@ -1687,10 +1823,9 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) {
   framer.set_enable_compression(false);
 
   {
-    const char kDescription[] = "SYN_STREAM frame, lowest pri, slot 2, no FIN";
+    const char kDescription[] = "SYN_STREAM frame, lowest pri, no FIN";
 
     const unsigned char kPri = IsSpdy2() ? 0xC0 : 0xE0;
-    const unsigned char kCre = IsSpdy2() ? 0 : 2;
     const unsigned char kV2FrameData[] = {
       0x80, spdy_version_ch_, 0x00, 0x01,
       0x00, 0x00, 0x00, 0x20,
@@ -1708,7 +1843,7 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) {
       0x00, 0x00, 0x00, 0x2a,
       0x00, 0x00, 0x00, 0x01,
       0x00, 0x00, 0x00, 0x00,
-      kPri, kCre, 0x00, 0x00,
+      kPri, 0x00, 0x00, 0x00,
       0x00, 0x02, 0x00, 0x00,
       0x00, 0x03, 'b',  'a',
       'r',  0x00, 0x00, 0x00,
@@ -1719,21 +1854,17 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) {
       'a',  'r'
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x2c, 0x08, 0x08,
-      0x00, 0x00, 0x00, 0x01,
-      0x00, 0x00, 0x00, 0x07,
-      0x00, 0x00, 0x00, 0x02,
-      0x00, 0x00, 0x00, 0x03,
-      'b',  'a',  'r',  0x00,
-      0x00, 0x00, 0x03, 'f',
-      'o',  'o',  0x00, 0x00,
-      0x00, 0x03, 'f',  'o',
-      'o',  0x00, 0x00, 0x00,
-      0x03, 'b', 'a', 'r'
+      0x00, 0x16, 0x01, 0x0c,  // HEADERS: PRIORITY | END_HEADERS
+      0x00, 0x00, 0x00, 0x01,  // Stream 1
+      0x00, 0x00, 0x00, 0x07,  // Priority 7
+      0x40, 0x03, 0x62, 0x61,  // @.ba
+      0x72, 0x03, 0x66, 0x6f,  // r.fo
+      0x6f, 0x40, 0x03, 0x66,  // o@.f
+      0x6f, 0x6f, 0x03, 0x62,  // oo.b
+      0x61, 0x72,              // ar
     };
     SpdySynStreamIR syn_stream(1);
     syn_stream.set_priority(framer.GetLowestPriority());
-    syn_stream.set_slot(kCre);
     syn_stream.SetHeader("bar", "foo");
     syn_stream.SetHeader("foo", "bar");
     scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
@@ -1778,17 +1909,13 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) {
       'b',  'a',  'r'
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x29, 0x08, 0x09,
-      0x7f, 0xff, 0xff, 0xff,
-      0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x02,
-      0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x03,
-      'f',  'o',  'o',  0x00,
-      0x00, 0x00, 0x03, 'f',
-      'o',  'o',  0x00, 0x00,
-      0x00, 0x03, 'b',  'a',
-      'r'
+      0x00, 0x13, 0x01, 0x0d,  // HEADERS: PRIORITY | FIN | END_HEADERS
+      0x7f, 0xff, 0xff, 0xff,  // Stream 0x7fffffff
+      0x00, 0x00, 0x00, 0x00,  // Priority 0
+      0x40, 0x00, 0x03, 0x66,  // @..f
+      0x6f, 0x6f, 0x40, 0x03,  // oo@.
+      0x66, 0x6f, 0x6f, 0x03,  // foo.
+      0x62, 0x61, 0x72,        // bar
     };
     SpdySynStreamIR syn_stream(0x7fffffff);
     syn_stream.set_associated_to_stream_id(0x7fffffff);
@@ -1839,17 +1966,13 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) {
       0x00, 0x00, 0x00
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x29, 0x08, 0x09,
-      0x7f, 0xff, 0xff, 0xff,
-      0x00, 0x00, 0x00, 0x01,
-      0x00, 0x00, 0x00, 0x02,
-      0x00, 0x00, 0x00, 0x03,
-      'b',  'a',  'r',  0x00,
-      0x00, 0x00, 0x03, 'f',
-      'o',  'o',  0x00, 0x00,
-      0x00, 0x03, 'f',  'o',
-      'o',  0x00, 0x00, 0x00,
-      0x00
+      0x00, 0x13, 0x01, 0x0d,  // HEADERS: PRIORITY | FIN | END_HEADERS
+      0x7f, 0xff, 0xff, 0xff,  // Stream 0x7fffffff
+      0x00, 0x00, 0x00, 0x01,  // Priority 1
+      0x40, 0x03, 0x62, 0x61,  // @.ba
+      0x72, 0x03, 0x66, 0x6f,  // r.fo
+      0x6f, 0x40, 0x03, 0x66,  // o@.f
+      0x6f, 0x6f, 0x00,        // oo.
     };
     SpdySynStreamIR syn_stream(0x7fffffff);
     syn_stream.set_associated_to_stream_id(0x7fffffff);
@@ -1916,23 +2039,6 @@ TEST_P(SpdyFramerTest, CreateSynStreamCompressed) {
       0x80, 0x00, 0x00, 0x00,
       0x00, 0xFF, 0xFF,
     };
-    const unsigned char kV4FrameData[] = {
-      0x00, 0x39, 0x08, 0x08,
-      0x00, 0x00, 0x00, 0x01,
-      0x00, 0x00, 0x00, 0x04,
-      0x38, 0xea, 0xe3, 0xc6,
-      0xa7, 0xc2, 0x02, 0xe5,
-      0x0e, 0x50, 0xc2, 0x4b,
-      0x4a, 0x04, 0xe5, 0x0b,
-      0x66, 0x80, 0x00, 0x4a,
-      0xcb, 0xcf, 0x07, 0x08,
-      0x20, 0x10, 0x95, 0x96,
-      0x9f, 0x0f, 0xa2, 0x00,
-      0x02, 0x28, 0x29, 0xb1,
-      0x08, 0x20, 0x80, 0x00,
-      0x00, 0x00, 0x00, 0xff,
-      0xff,
-    };
     SpdySynStreamIR syn_stream(1);
     syn_stream.set_priority(priority);
     syn_stream.SetHeader("bar", "foo");
@@ -1943,7 +2049,7 @@ TEST_P(SpdyFramerTest, CreateSynStreamCompressed) {
     } else if (IsSpdy3()) {
       CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
     } else {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+      // Deflate compression doesn't apply to HPACK.
     }
   }
 }
@@ -1981,16 +2087,13 @@ TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) {
       0x03, 'b',  'a',  'r'
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x28, 0x08, 0x00,
-      0x00, 0x00, 0x00, 0x01,
-      0x00, 0x00, 0x00, 0x02,
-      0x00, 0x00, 0x00, 0x03,
-      'b',  'a',  'r',  0x00,
-      0x00, 0x00, 0x03, 'f',
-      'o',  'o',  0x00, 0x00,
-      0x00, 0x03, 'f',  'o',
-      'o',  0x00, 0x00, 0x00,
-      0x03, 'b',  'a',  'r'
+      0x00, 0x12, 0x01, 0x04,  // HEADER: END_HEADERS
+      0x00, 0x00, 0x00, 0x01,  // Stream 1
+      0x40, 0x03, 0x62, 0x61,  // @.ba
+      0x72, 0x03, 0x66, 0x6f,  // r.fo
+      0x6f, 0x40, 0x03, 0x66,  // o@.f
+      0x6f, 0x6f, 0x03, 0x62,  // oo.b
+      0x61, 0x72,              // ar
     };
     SpdySynReplyIR syn_reply(1);
     syn_reply.SetHeader("bar", "foo");
@@ -2034,16 +2137,12 @@ TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) {
       'r'
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x25, 0x08, 0x01,
-      0x7f, 0xff, 0xff, 0xff,
-      0x00, 0x00, 0x00, 0x02,
-      0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x03,
-      'f',  'o',  'o',  0x00,
-      0x00, 0x00, 0x03, 'f',
-      'o',  'o',  0x00, 0x00,
-      0x00, 0x03, 'b',  'a',
-      'r'
+      0x00, 0x0f, 0x01, 0x05,  // HEADER: FIN | END_HEADERS
+      0x7f, 0xff, 0xff, 0xff,  // Stream 0x7fffffff
+      0x40, 0x00, 0x03, 0x66,  // @..f
+      0x6f, 0x6f, 0x40, 0x03,  // oo@.
+      0x66, 0x6f, 0x6f, 0x03,  // foo.
+      0x62, 0x61, 0x72,        // bar
     };
     SpdySynReplyIR syn_reply(0x7fffffff);
     syn_reply.set_fin(true);
@@ -2088,16 +2187,12 @@ TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) {
       0x00
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x25, 0x08, 0x01,
-      0x7f, 0xff, 0xff, 0xff,
-      0x00, 0x00, 0x00, 0x02,
-      0x00, 0x00, 0x00, 0x03,
-      'b',  'a',  'r',  0x00,
-      0x00, 0x00, 0x03, 'f',
-      'o',  'o',  0x00, 0x00,
-      0x00, 0x03, 'f',  'o',
-      'o',  0x00, 0x00, 0x00,
-      0x00
+      0x00, 0x0f, 0x01, 0x05,  // HEADER: FIN | END_HEADERS
+      0x7f, 0xff, 0xff, 0xff,  // Stream 0x7fffffff
+      0x40, 0x03, 0x62, 0x61,  // @.ba
+      0x72, 0x03, 0x66, 0x6f,  // r.fo
+      0x6f, 0x40, 0x03, 0x66,  // o@.f
+      0x6f, 0x6f, 0x00,        // oo.
     };
     SpdySynReplyIR syn_reply(0x7fffffff);
     syn_reply.set_fin(true);
@@ -2158,22 +2253,6 @@ TEST_P(SpdyFramerTest, CreateSynReplyCompressed) {
       0x00, 0x00, 0x00, 0xff,
       0xff,
     };
-    const unsigned char kV4FrameData[] = {
-      0x00, 0x35, 0x08, 0x00,
-      0x00, 0x00, 0x00, 0x01,
-      0x38, 0xea, 0xe3, 0xc6,
-      0xa7, 0xc2, 0x02, 0xe5,
-      0x0e, 0x50, 0xc2, 0x4b,
-      0x4a, 0x04, 0xe5, 0x0b,
-      0x66, 0x80, 0x00, 0x4a,
-      0xcb, 0xcf, 0x07, 0x08,
-      0x20, 0x10, 0x95, 0x96,
-      0x9f, 0x0f, 0xa2, 0x00,
-      0x02, 0x28, 0x29, 0xb1,
-      0x08, 0x20, 0x80, 0x00,
-      0x00, 0x00, 0x00, 0xff,
-      0xff,
-    };
     SpdySynReplyIR syn_reply(1);
     syn_reply.SetHeader("bar", "foo");
     syn_reply.SetHeader("foo", "bar");
@@ -2183,7 +2262,7 @@ TEST_P(SpdyFramerTest, CreateSynReplyCompressed) {
     } else if (IsSpdy3()) {
       CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
     } else {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+      // Deflate compression doesn't apply to HPACK.
     }
   }
 }
@@ -2201,24 +2280,14 @@ TEST_P(SpdyFramerTest, CreateRstStream) {
       0x00, 0x00, 0x00, 0x01,
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x0f, 0x03, 0x00,
+      0x00, 0x07, 0x03, 0x00,
       0x00, 0x00, 0x00, 0x01,
       0x00, 0x00, 0x00, 0x01,
       0x52, 0x53, 0x54
     };
-    const unsigned char kV4FrameDataOld[] = {
-      0x00, 0x0c, 0x03, 0x00,
-      0x00, 0x00, 0x00, 0x01,
-      0x00, 0x00, 0x00, 0x01,
-    };
-    net::SpdyRstStreamIR rst_stream(1, RST_STREAM_PROTOCOL_ERROR, "RST");
+    SpdyRstStreamIR rst_stream(1, RST_STREAM_PROTOCOL_ERROR, "RST");
     scoped_ptr<SpdyFrame> frame(framer.SerializeRstStream(rst_stream));
-    // TODO(jgraettinger): We expect to handle but not emit RST payloads.
-    if (IsSpdy4() &&
-        true /*!FLAGS_gfe2_restart_flag_allow_spdy_4_rst_stream_opaque_data*/) {
-      CompareFrame(kDescription, *frame, kV4FrameDataOld,
-                   arraysize(kV4FrameDataOld));
-    } else if (IsSpdy4()) {
+    if (IsSpdy4()) {
       CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
     } else {
       CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
@@ -2234,13 +2303,13 @@ TEST_P(SpdyFramerTest, CreateRstStream) {
       0x00, 0x00, 0x00, 0x01,
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x0c, 0x03, 0x00,
+      0x00, 0x04, 0x03, 0x00,
       0x7f, 0xff, 0xff, 0xff,
       0x00, 0x00, 0x00, 0x01,
     };
-    net::SpdyRstStreamIR rst_stream(0x7FFFFFFF,
-                                    RST_STREAM_PROTOCOL_ERROR,
-                                    "");
+    SpdyRstStreamIR rst_stream(0x7FFFFFFF,
+                               RST_STREAM_PROTOCOL_ERROR,
+                               "");
     scoped_ptr<SpdyFrame> frame(framer.SerializeRstStream(rst_stream));
     if (IsSpdy4()) {
       CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
@@ -2258,13 +2327,13 @@ TEST_P(SpdyFramerTest, CreateRstStream) {
       0x00, 0x00, 0x00, 0x06,
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x0c, 0x03, 0x00,
+      0x00, 0x04, 0x03, 0x00,
       0x7f, 0xff, 0xff, 0xff,
       0x00, 0x00, 0x00, 0x06,
     };
-    net::SpdyRstStreamIR rst_stream(0x7FFFFFFF,
-                                    RST_STREAM_INTERNAL_ERROR,
-                                    "");
+    SpdyRstStreamIR rst_stream(0x7FFFFFFF,
+                               RST_STREAM_INTERNAL_ERROR,
+                               "");
     scoped_ptr<SpdyFrame> frame(framer.SerializeRstStream(rst_stream));
     if (IsSpdy4()) {
       CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
@@ -2280,43 +2349,41 @@ TEST_P(SpdyFramerTest, CreateSettings) {
   {
     const char kDescription[] = "Network byte order SETTINGS frame";
 
-    uint32 kValue = 0x0a0b0c0d;
-    SpdySettingsFlags kFlags = static_cast<SpdySettingsFlags>(0x01);
-    SpdySettingsIds kId = static_cast<SpdySettingsIds>(0x020304);
-
-    SettingsMap settings;
-    settings[kId] = SettingsFlagsAndValue(kFlags, kValue);
-
-    EXPECT_EQ(kFlags, settings[kId].first);
-    EXPECT_EQ(kValue, settings[kId].second);
-
     const unsigned char kV2FrameData[] = {
       0x80, spdy_version_ch_, 0x00, 0x04,
       0x00, 0x00, 0x00, 0x0c,
       0x00, 0x00, 0x00, 0x01,
-      0x04, 0x03, 0x02, 0x01,
+      0x07, 0x00, 0x00, 0x01,
       0x0a, 0x0b, 0x0c, 0x0d,
     };
     const unsigned char kV3FrameData[] = {
       0x80, spdy_version_ch_, 0x00, 0x04,
       0x00, 0x00, 0x00, 0x0c,
       0x00, 0x00, 0x00, 0x01,
-      0x01, 0x02, 0x03, 0x04,
+      0x01, 0x00, 0x00, 0x07,
       0x0a, 0x0b, 0x0c, 0x0d,
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x14, 0x04, 0x00,
+      0x00, 0x05, 0x04, 0x00,
       0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x01,
-      0x01, 0x02, 0x03, 0x04,
-      0x0a, 0x0b, 0x0c, 0x0d,
+      0x04, 0x0a, 0x0b, 0x0c,
+      0x0d,
     };
 
+    uint32 kValue = 0x0a0b0c0d;
     SpdySettingsIR settings_ir;
+
+    SpdySettingsFlags kFlags = static_cast<SpdySettingsFlags>(0x01);
+    SpdySettingsIds kId = SETTINGS_INITIAL_WINDOW_SIZE;
+    SettingsMap settings;
+    settings[kId] = SettingsFlagsAndValue(kFlags, kValue);
+    EXPECT_EQ(kFlags, settings[kId].first);
+    EXPECT_EQ(kValue, settings[kId].second);
     settings_ir.AddSetting(kId,
                            kFlags & SETTINGS_FLAG_PLEASE_PERSIST,
                            kFlags & SETTINGS_FLAG_PERSISTED,
                            kValue);
+
     scoped_ptr<SpdyFrame> frame(framer.SerializeSettings(settings_ir));
     if (IsSpdy2()) {
       CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
@@ -2330,56 +2397,73 @@ TEST_P(SpdyFramerTest, CreateSettings) {
   {
     const char kDescription[] = "Basic SETTINGS frame";
 
-    SettingsMap settings;
-    AddSpdySettingFromWireFormat(
-        &settings, 0x00000000, 0x00000001);  // 1st Setting
-    AddSpdySettingFromWireFormat(
-        &settings, 0x01000001, 0x00000002);  // 2nd Setting
-    AddSpdySettingFromWireFormat(
-        &settings, 0x02000002, 0x00000003);  // 3rd Setting
-    AddSpdySettingFromWireFormat(
-        &settings, 0x03000003, 0xff000004);  // 4th Setting
-
-    const unsigned char kV3FrameData[] = {  // Also applies for V2.
+    const unsigned char kV2FrameData[] = {
       0x80, spdy_version_ch_, 0x00, 0x04,
       0x00, 0x00, 0x00, 0x24,
       0x00, 0x00, 0x00, 0x04,
-      0x00, 0x00, 0x00, 0x00,  // 1st Setting
-      0x00, 0x00, 0x00, 0x01,
-      0x01, 0x00, 0x00, 0x01,  // 2nd Setting
-      0x00, 0x00, 0x00, 0x02,
-      0x02, 0x00, 0x00, 0x02,  // 3rd Setting
-      0x00, 0x00, 0x00, 0x03,
-      0x03, 0x00, 0x00, 0x03,  // 4th Setting
-      0xff, 0x00, 0x00, 0x04,
+      0x01, 0x00, 0x00, 0x00,  // 1st Setting
+      0x00, 0x00, 0x00, 0x05,
+      0x02, 0x00, 0x00, 0x00,  // 2nd Setting
+      0x00, 0x00, 0x00, 0x06,
+      0x03, 0x00, 0x00, 0x00,  // 3rd Setting
+      0x00, 0x00, 0x00, 0x07,
+      0x04, 0x00, 0x00, 0x00,  // 4th Setting
+      0x00, 0x00, 0x00, 0x08,
     };
+    const unsigned char kV3FrameData[] = {
+      0x80, spdy_version_ch_, 0x00, 0x04,
+      0x00, 0x00, 0x00, 0x24,
+      0x00, 0x00, 0x00, 0x04,
+      0x00, 0x00, 0x00, 0x01,  // 1st Setting
+      0x00, 0x00, 0x00, 0x05,
+      0x00, 0x00, 0x00, 0x02,  // 2nd Setting
+      0x00, 0x00, 0x00, 0x06,
+      0x00, 0x00, 0x00, 0x03,  // 3rd Setting
+      0x00, 0x00, 0x00, 0x07,
+      0x00, 0x00, 0x00, 0x04,  // 4th Setting
+      0x00, 0x00, 0x00, 0x08,
+    };
+    // These end up seemingly out of order because of the way that our internal
+    // ordering for settings_ir works. HTTP2 has no requirement on ordering on
+    // the wire.
     const unsigned char kV4FrameData[] = {
-      0x00, 0x2c, 0x04, 0x00,
+      0x00, 0x14, 0x04, 0x00,
       0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x04,
-      0x00, 0x00, 0x00, 0x00,  // 1st Setting
-      0x00, 0x00, 0x00, 0x01,
-      0x01, 0x00, 0x00, 0x01,  // 2nd Setting
-      0x00, 0x00, 0x00, 0x02,
-      0x02, 0x00, 0x00, 0x02,  // 3rd Setting
-      0x00, 0x00, 0x00, 0x03,
-      0x03, 0x00, 0x00, 0x03,  // 4th Setting
-      0xff, 0x00, 0x00, 0x04,
+      0x03,                    // 3rd Setting
+      0x00, 0x00, 0x00, 0x07,
+      0x04,                    // 4th Setting
+      0x00, 0x00, 0x00, 0x08,
+      0x01,                    // 1st Setting
+      0x00, 0x00, 0x00, 0x05,
+      0x02,                    // 2nd Setting
+      0x00, 0x00, 0x00, 0x06,
     };
+
     SpdySettingsIR settings_ir;
-    for (SettingsMap::const_iterator it = settings.begin();
-         it != settings.end();
-         ++it) {
-      settings_ir.AddSetting(it->first,
-                             it->second.first & SETTINGS_FLAG_PLEASE_PERSIST,
-                             it->second.first & SETTINGS_FLAG_PERSISTED,
-                             it->second.second);
-    }
+    settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 1),
+                           false,  // persist
+                           false,  // persisted
+                           5);
+    settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 2),
+                           false,  // persist
+                           false,  // persisted
+                           6);
+    settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 3),
+                           false,  // persist
+                           false,  // persisted
+                           7);
+    settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 4),
+                           false,  // persist
+                           false,  // persisted
+                           8);
     scoped_ptr<SpdyFrame> frame(framer.SerializeSettings(settings_ir));
-    if (IsSpdy4()) {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
-    } else {
+
+    if (IsSpdy2()) {
+      CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData));
+    } else if (IsSpdy3()) {
       CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
+    } else {
+      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
     }
   }
 
@@ -2392,8 +2476,7 @@ TEST_P(SpdyFramerTest, CreateSettings) {
       0x00, 0x00, 0x00, 0x00,
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x0c, 0x04, 0x00,
-      0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x04, 0x00,
       0x00, 0x00, 0x00, 0x00,
     };
     SpdySettingsIR settings_ir;
@@ -2417,14 +2500,34 @@ TEST_P(SpdyFramerTest, CreatePingFrame) {
       0x12, 0x34, 0x56, 0x78,
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x0c, 0x06, 0x00,
+      0x00, 0x08, 0x06, 0x00,
+      0x00, 0x00, 0x00, 0x00,
+      0x12, 0x34, 0x56, 0x78,
+      0x9a, 0xbc, 0xde, 0xff,
+    };
+    const unsigned char kV4FrameDataWithAck[] = {
+      0x00, 0x08, 0x06, 0x01,
       0x00, 0x00, 0x00, 0x00,
       0x12, 0x34, 0x56, 0x78,
+      0x9a, 0xbc, 0xde, 0xff,
     };
-    scoped_ptr<SpdyFrame> frame(framer.SerializePing(SpdyPingIR(0x12345678u)));
+    scoped_ptr<SpdyFrame> frame;
     if (IsSpdy4()) {
+      const SpdyPingId kPingId = 0x123456789abcdeffULL;
+      SpdyPingIR ping_ir(kPingId);
+      // Tests SpdyPingIR when the ping is not an ack.
+      ASSERT_FALSE(ping_ir.is_ack());
+      frame.reset(framer.SerializePing(ping_ir));
       CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+
+      // Tests SpdyPingIR when the ping is an ack.
+      ping_ir.set_is_ack(true);
+      frame.reset(framer.SerializePing(ping_ir));
+      CompareFrame(kDescription, *frame,
+                   kV4FrameDataWithAck, arraysize(kV4FrameDataWithAck));
+
     } else {
+      frame.reset(framer.SerializePing(SpdyPingIR(0x12345678ull)));
       CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
     }
   }
@@ -2447,7 +2550,7 @@ TEST_P(SpdyFramerTest, CreateGoAway) {
       0x00, 0x00, 0x00, 0x00,  // Status
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x12, 0x07, 0x00,
+      0x00, 0x0a, 0x07, 0x00,
       0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00,  // Stream id
       0x00, 0x00, 0x00, 0x00,  // Status
@@ -2478,7 +2581,7 @@ TEST_P(SpdyFramerTest, CreateGoAway) {
       0x00, 0x00, 0x00, 0x02,  // Status
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x12, 0x07, 0x00,
+      0x00, 0x0a, 0x07, 0x00,
       0x00, 0x00, 0x00, 0x00,
       0x7f, 0xff, 0xff, 0xff,  // Stream Id
       0x00, 0x00, 0x00, 0x02,  // Status
@@ -2528,16 +2631,13 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) {
       0x03, 'b',  'a',  'r'
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x28, 0x08, 0x00,
-      0x00, 0x00, 0x00, 0x01,
-      0x00, 0x00, 0x00, 0x02,
-      0x00, 0x00, 0x00, 0x03,
-      'b',  'a',  'r',  0x00,
-      0x00, 0x00, 0x03, 'f',
-      'o',  'o',  0x00, 0x00,
-      0x00, 0x03, 'f',  'o',
-      'o',  0x00, 0x00, 0x00,
-      0x03, 'b',  'a',  'r'
+       0x00, 0x12, 0x01, 0x04,  // Headers: END_HEADERS
+       0x00, 0x00, 0x00, 0x01,  // Stream 1
+       0x40, 0x03, 0x62, 0x61,  // @.ba
+       0x72, 0x03, 0x66, 0x6f,  // r.fo
+       0x6f, 0x40, 0x03, 0x66,  // o@.f
+       0x6f, 0x6f, 0x03, 0x62,  // oo.b
+       0x61, 0x72,              // ar
     };
     SpdyHeadersIR headers_ir(1);
     headers_ir.SetHeader("bar", "foo");
@@ -2581,16 +2681,12 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) {
       'r'
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x25, 0x08, 0x01,
-      0x7f, 0xff, 0xff, 0xff,
-      0x00, 0x00, 0x00, 0x02,
-      0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x03,
-      'f',  'o',  'o',  0x00,
-      0x00, 0x00, 0x03, 'f',
-      'o',  'o',  0x00, 0x00,
-      0x00, 0x03, 'b',  'a',
-      'r'
+      0x00, 0x0f, 0x01, 0x05,  // HEADER: FIN | END_HEADERS
+      0x7f, 0xff, 0xff, 0xff,  // Stream 0x7fffffff
+      0x40, 0x00, 0x03, 0x66,  // @..f
+      0x6f, 0x6f, 0x40, 0x03,  // oo@.
+      0x66, 0x6f, 0x6f, 0x03,  // foo.
+      0x62, 0x61, 0x72,        // bar
     };
     SpdyHeadersIR headers_ir(0x7fffffff);
     headers_ir.set_fin(true);
@@ -2635,16 +2731,12 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) {
       0x00
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x25, 0x08, 0x01,
-      0x7f, 0xff, 0xff, 0xff,
-      0x00, 0x00, 0x00, 0x02,
-      0x00, 0x00, 0x00, 0x03,
-      'b',  'a',  'r',  0x00,
-      0x00, 0x00, 0x03, 'f',
-      'o',  'o',  0x00, 0x00,
-      0x00, 0x03, 'f',  'o',
-      'o',  0x00, 0x00, 0x00,
-      0x00
+      0x00, 0x0f, 0x01, 0x05,  // HEADER: FIN | END_HEADERS
+      0x7f, 0xff, 0xff, 0xff,  // Stream 0x7fffffff
+      0x40, 0x03, 0x62, 0x61,  // @.ba
+      0x72, 0x03, 0x66, 0x6f,  // r.fo
+      0x6f, 0x40, 0x03, 0x66,  // o@.f
+      0x6f, 0x6f, 0x00,        // oo.
     };
     SpdyHeadersIR headers_ir(0x7fffffff);
     headers_ir.set_fin(true);
@@ -2705,22 +2797,6 @@ TEST_P(SpdyFramerTest, CreateHeadersCompressed) {
       0x00, 0x00, 0x00, 0xff,
       0xff,
     };
-    const unsigned char kV4FrameData[] = {
-      0x00, 0x35, 0x08, 0x00,
-      0x00, 0x00, 0x00, 0x01,
-      0x38, 0xea, 0xe3, 0xc6,
-      0xa7, 0xc2, 0x02, 0xe5,
-      0x0e, 0x50, 0xc2, 0x4b,
-      0x4a, 0x04, 0xe5, 0x0b,
-      0x66, 0x80, 0x00, 0x4a,
-      0xcb, 0xcf, 0x07, 0x08,
-      0x20, 0x10, 0x95, 0x96,
-      0x9f, 0x0f, 0xa2, 0x00,
-      0x02, 0x28, 0x29, 0xb1,
-      0x08, 0x20, 0x80, 0x00,
-      0x00, 0x00, 0x00, 0xff,
-      0xff
-    };
     SpdyHeadersIR headers_ir(1);
     headers_ir.SetHeader("bar", "foo");
     headers_ir.SetHeader("foo", "bar");
@@ -2730,7 +2806,7 @@ TEST_P(SpdyFramerTest, CreateHeadersCompressed) {
     } else if (IsSpdy3()) {
       CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData));
     } else {
-      CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
+      // Deflate compression doesn't apply to HPACK.
     }
   }
 }
@@ -2748,12 +2824,12 @@ TEST_P(SpdyFramerTest, CreateWindowUpdate) {
       0x00, 0x00, 0x00, 0x01,
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x0c, 0x09, 0x00,
+      0x00, 0x04, 0x08, 0x00,
       0x00, 0x00, 0x00, 0x01,
       0x00, 0x00, 0x00, 0x01,
     };
     scoped_ptr<SpdyFrame> frame(
-        framer.SerializeWindowUpdate(net::SpdyWindowUpdateIR(1, 1)));
+        framer.SerializeWindowUpdate(SpdyWindowUpdateIR(1, 1)));
     if (IsSpdy4()) {
       CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
     } else {
@@ -2770,12 +2846,12 @@ TEST_P(SpdyFramerTest, CreateWindowUpdate) {
       0x00, 0x00, 0x00, 0x01,
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x0c, 0x09, 0x00,
+      0x00, 0x04, 0x08, 0x00,
       0x7f, 0xff, 0xff, 0xff,
       0x00, 0x00, 0x00, 0x01,
     };
     scoped_ptr<SpdyFrame> frame(framer.SerializeWindowUpdate(
-        net::SpdyWindowUpdateIR(0x7FFFFFFF, 1)));
+        SpdyWindowUpdateIR(0x7FFFFFFF, 1)));
     if (IsSpdy4()) {
       CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
     } else {
@@ -2792,12 +2868,12 @@ TEST_P(SpdyFramerTest, CreateWindowUpdate) {
       0x7f, 0xff, 0xff, 0xff,
     };
     const unsigned char kV4FrameData[] = {
-      0x00, 0x0c, 0x09, 0x00,
+      0x00, 0x04, 0x08, 0x00,
       0x00, 0x00, 0x00, 0x01,
       0x7f, 0xff, 0xff, 0xff,
     };
     scoped_ptr<SpdyFrame> frame(framer.SerializeWindowUpdate(
-        net::SpdyWindowUpdateIR(1, 0x7FFFFFFF)));
+        SpdyWindowUpdateIR(1, 0x7FFFFFFF)));
     if (IsSpdy4()) {
       CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData));
     } else {
@@ -2815,7 +2891,7 @@ TEST_P(SpdyFramerTest, SerializeBlocked) {
 
   const char kDescription[] = "BLOCKED frame";
   const unsigned char kFrameData[] = {
-    0x00, 0x08, 0x0b, 0x00,
+    0x00, 0x00, 0x0a, 0x00,
     0x00, 0x00, 0x00, 0x00,
   };
   SpdyBlockedIR blocked_ir(0);
@@ -2849,57 +2925,17 @@ TEST_P(SpdyFramerTest, CreatePushPromiseUncompressed) {
 
   SpdyFramer framer(spdy_version_);
   framer.set_enable_compression(false);
-
   const char kDescription[] = "PUSH_PROMISE frame";
 
   const unsigned char kFrameData[] = {
-    0x00, 0x2C, 0x0C, 0x00,  // length = 44, type = 12, flags = 0
-    0x00, 0x00, 0x00, 0x2A,  // stream id = 42
-    0x00, 0x00, 0x00, 0x39,  // promised stream id = 57
-    0x00, 0x00, 0x00, 0x02,  // start of uncompressed header block
-    0x00, 0x00, 0x00, 0x03,
-    'b',  'a',  'r',  0x00,
-    0x00, 0x00, 0x03, 'f',
-    'o',  'o',  0x00, 0x00,
-    0x00, 0x03, 'f',  'o',
-    'o',  0x00, 0x00, 0x00,
-    0x03, 'b',  'a',  'r'    // end of uncompressed header block
-  };
-
-  SpdyPushPromiseIR push_promise(42, 57);
-  push_promise.SetHeader("bar", "foo");
-  push_promise.SetHeader("foo", "bar");
-  scoped_ptr<SpdySerializedFrame> frame(
-    framer.SerializePushPromise(push_promise));
-  CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
-}
-
-TEST_P(SpdyFramerTest, CreatePushPromiseCompressed) {
-  if (spdy_version_ < SPDY4) {
-    return;
-  }
-
-  SpdyFramer framer(spdy_version_);
-  framer.set_enable_compression(true);
-
-  const char kDescription[] = "PUSH_PROMISE frame";
-
-  const unsigned char kFrameData[] = {
-    0x00, 0x39, 0x0C, 0x00,  // length = 57, type = 12, flags = 0
-    0x00, 0x00, 0x00, 0x2A,  // stream id = 42
-    0x00, 0x00, 0x00, 0x39,  // promised stream id = 57
-    0x38, 0xea, 0xe3, 0xc6,  // start of compressed header block
-    0xa7, 0xc2, 0x02, 0xe5,
-    0x0e, 0x50, 0xc2, 0x4b,
-    0x4a, 0x04, 0xe5, 0x0b,
-    0x66, 0x80, 0x00, 0x4a,
-    0xcb, 0xcf, 0x07, 0x08,
-    0x20, 0x10, 0x95, 0x96,
-    0x9f, 0x0f, 0xa2, 0x00,
-    0x02, 0x28, 0x29, 0xb1,
-    0x08, 0x20, 0x80, 0x00,
-    0x00, 0x00, 0x00, 0xff,
-    0xff                     // end of compressed header block
+    0x00, 0x16, 0x05, 0x04,  // PUSH_PROMISE: END_HEADERS
+    0x00, 0x00, 0x00, 0x2a,  // Stream 42
+    0x00, 0x00, 0x00, 0x39,  // Promised stream 57
+    0x40, 0x03, 0x62, 0x61,  // @.ba
+    0x72, 0x03, 0x66, 0x6f,  // r.fo
+    0x6f, 0x40, 0x03, 0x66,  // o@.f
+    0x6f, 0x6f, 0x03, 0x62,  // oo.b
+    0x61, 0x72,              // ar
   };
 
   SpdyPushPromiseIR push_promise(42, 57);
@@ -3001,6 +3037,10 @@ TEST_P(SpdyFramerTest, ReadCompressedHeadersHeaderBlockWithHalfClose) {
 }
 
 TEST_P(SpdyFramerTest, ControlFrameAtMaxSizeLimit) {
+  if (spdy_version_ > SPDY3) {
+    // TODO(jgraettinger): This test setup doesn't work with HPACK.
+    return;
+  }
   // First find the size of the header value in order to just reach the control
   // frame max size.
   SpdyFramer framer(spdy_version_);
@@ -3012,7 +3052,7 @@ TEST_P(SpdyFramerTest, ControlFrameAtMaxSizeLimit) {
   const size_t kBigValueSize =
       framer.GetControlFrameBufferMaxSize() - control_frame->size();
 
-  // Create a frame at exatly that size.
+  // Create a frame at exactly that size.
   string big_value(kBigValueSize, 'x');
   syn_stream.SetHeader("aa", big_value.c_str());
   control_frame.reset(framer.SerializeSynStream(syn_stream));
@@ -3032,6 +3072,10 @@ TEST_P(SpdyFramerTest, ControlFrameAtMaxSizeLimit) {
 }
 
 TEST_P(SpdyFramerTest, ControlFrameTooLarge) {
+  if (spdy_version_ > SPDY3) {
+    // TODO(jgraettinger): This test setup doesn't work with HPACK.
+    return;
+  }
   // First find the size of the header value in order to just reach the control
   // frame max size.
   SpdyFramer framer(spdy_version_);
@@ -3046,7 +3090,11 @@ TEST_P(SpdyFramerTest, ControlFrameTooLarge) {
   // Create a frame at exatly that size.
   string big_value(kBigValueSize, 'x');
   syn_stream.SetHeader("aa", big_value.c_str());
+  // Upstream branches here and wraps SPDY4 with EXPECT_DEBUG_DFATAL. We
+  // neither support that in Chromium, nor do we use the same DFATAL (see
+  // SpdyFrameBuilder::WriteFramePrefix()).
   control_frame.reset(framer.SerializeSynStream(syn_stream));
+
   EXPECT_TRUE(control_frame.get() != NULL);
   EXPECT_EQ(framer.GetControlFrameBufferMaxSize() + 1,
             control_frame->size());
@@ -3064,38 +3112,95 @@ TEST_P(SpdyFramerTest, ControlFrameTooLarge) {
   EXPECT_EQ(0u, visitor.header_buffer_length_);
 }
 
-// Check that the framer stops delivering header data chunks once the visitor
-// declares it doesn't want any more. This is important to guard against
-// "zip bomb" types of attacks.
-TEST_P(SpdyFramerTest, ControlFrameMuchTooLarge) {
-  const size_t kHeaderBufferChunks = 4;
-  const size_t kHeaderBufferSize =
-      TestSpdyVisitor::header_data_chunk_max_size() * kHeaderBufferChunks;
-  const size_t kBigValueSize = kHeaderBufferSize * 2;
-  string big_value(kBigValueSize, 'x');
+TEST_P(SpdyFramerTest, TooLargeHeadersFrameUsesContinuation) {
+  if (spdy_version_ < SPDY4) {
+    return;
+  }
   SpdyFramer framer(spdy_version_);
-  SpdySynStreamIR syn_stream(1);
-  syn_stream.set_priority(1);
-  syn_stream.set_fin(true);
-  syn_stream.SetHeader("aa", big_value.c_str());
-  scoped_ptr<SpdyFrame> control_frame(framer.SerializeSynStream(syn_stream));
+  framer.set_enable_compression(false);
+  SpdyHeadersIR headers(1);
+
+  // Exact payload length will change with HPACK, but this should be long
+  // enough to cause an overflow.
+  const size_t kBigValueSize = framer.GetControlFrameBufferMaxSize();
+  string big_value(kBigValueSize, 'x');
+  headers.SetHeader("aa", big_value.c_str());
+  scoped_ptr<SpdyFrame> control_frame(framer.SerializeHeaders(headers));
   EXPECT_TRUE(control_frame.get() != NULL);
+  EXPECT_GT(control_frame->size(), framer.GetControlFrameBufferMaxSize());
+
   TestSpdyVisitor visitor(spdy_version_);
-  visitor.set_header_buffer_size(kHeaderBufferSize);
-  visitor.use_compression_ = true;
   visitor.SimulateInFramer(
       reinterpret_cast<unsigned char*>(control_frame->data()),
       control_frame->size());
-  EXPECT_FALSE(visitor.header_buffer_valid_);
-  EXPECT_EQ(1, visitor.error_count_);
-  EXPECT_EQ(SpdyFramer::SPDY_CONTROL_PAYLOAD_TOO_LARGE,
-            visitor.framer_.error_code())
-      << SpdyFramer::ErrorCodeToString(framer.error_code());
+  EXPECT_TRUE(visitor.header_buffer_valid_);
+  EXPECT_EQ(0, visitor.error_count_);
+  EXPECT_EQ(1, visitor.headers_frame_count_);
+  EXPECT_EQ(1, visitor.continuation_count_);
+  EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_);
+}
 
-  // The framer should have stoped delivering chunks after the visitor
-  // signaled "stop" by returning false from OnControlFrameHeaderData().
-  //
-  // control_frame_header_data_count_ depends on the random sequence
+TEST_P(SpdyFramerTest, TooLargePushPromiseFrameUsesContinuation) {
+  if (spdy_version_ < SPDY4) {
+    return;
+  }
+  SpdyFramer framer(spdy_version_);
+  framer.set_enable_compression(false);
+  SpdyPushPromiseIR push_promise(1, 2);
+
+  // Exact payload length will change with HPACK, but this should be long
+  // enough to cause an overflow.
+  const size_t kBigValueSize = framer.GetControlFrameBufferMaxSize();
+  string big_value(kBigValueSize, 'x');
+  push_promise.SetHeader("aa", big_value.c_str());
+  scoped_ptr<SpdyFrame> control_frame(
+      framer.SerializePushPromise(push_promise));
+  EXPECT_TRUE(control_frame.get() != NULL);
+  EXPECT_GT(control_frame->size(), framer.GetControlFrameBufferMaxSize());
+
+  TestSpdyVisitor visitor(spdy_version_);
+  visitor.SimulateInFramer(
+      reinterpret_cast<unsigned char*>(control_frame->data()),
+      control_frame->size());
+  EXPECT_TRUE(visitor.header_buffer_valid_);
+  EXPECT_EQ(0, visitor.error_count_);
+  EXPECT_EQ(1, visitor.push_promise_frame_count_);
+  EXPECT_EQ(1, visitor.continuation_count_);
+  EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_);
+}
+
+// Check that the framer stops delivering header data chunks once the visitor
+// declares it doesn't want any more. This is important to guard against
+// "zip bomb" types of attacks.
+TEST_P(SpdyFramerTest, ControlFrameMuchTooLarge) {
+  const size_t kHeaderBufferChunks = 4;
+  const size_t kHeaderBufferSize =
+      TestSpdyVisitor::header_data_chunk_max_size() * kHeaderBufferChunks;
+  const size_t kBigValueSize = kHeaderBufferSize * 2;
+  string big_value(kBigValueSize, 'x');
+  SpdyFramer framer(spdy_version_);
+  SpdySynStreamIR syn_stream(1);
+  syn_stream.set_priority(1);
+  syn_stream.set_fin(true);
+  syn_stream.SetHeader("aa", big_value.c_str());
+  scoped_ptr<SpdyFrame> control_frame(framer.SerializeSynStream(syn_stream));
+  EXPECT_TRUE(control_frame.get() != NULL);
+  TestSpdyVisitor visitor(spdy_version_);
+  visitor.set_header_buffer_size(kHeaderBufferSize);
+  visitor.use_compression_ = true;
+  visitor.SimulateInFramer(
+      reinterpret_cast<unsigned char*>(control_frame->data()),
+      control_frame->size());
+  EXPECT_FALSE(visitor.header_buffer_valid_);
+  EXPECT_EQ(1, visitor.error_count_);
+  EXPECT_EQ(SpdyFramer::SPDY_CONTROL_PAYLOAD_TOO_LARGE,
+            visitor.framer_.error_code())
+      << SpdyFramer::ErrorCodeToString(framer.error_code());
+
+  // The framer should have stoped delivering chunks after the visitor
+  // signaled "stop" by returning false from OnControlFrameHeaderData().
+  //
+  // control_frame_header_data_count_ depends on the random sequence
   // produced by rand(), so adding, removing or running single tests
   // alters this value.  The best we can do is assert that it happens
   // at least kHeaderBufferChunks + 1.
@@ -3108,6 +3213,10 @@ TEST_P(SpdyFramerTest, ControlFrameMuchTooLarge) {
 }
 
 TEST_P(SpdyFramerTest, DecompressCorruptHeaderBlock) {
+  if (spdy_version_ > SPDY3) {
+    // Deflate compression doesn't apply to HPACK.
+    return;
+  }
   SpdyFramer framer(spdy_version_);
   framer.set_enable_compression(false);
   // Construct a SYN_STREAM control frame without compressing the header block,
@@ -3143,7 +3252,8 @@ TEST_P(SpdyFramerTest, ControlFrameSizesAreValidated) {
 
   // SPDY version 4 and up GOAWAY frames are only bound to a minimal length,
   // since it may carry opaque data. Verify that minimal length is tested.
-  const unsigned char less_than_min_length =  framer.GetGoAwayMinimumSize() - 1;
+  const unsigned char less_than_min_length =
+      framer.GetGoAwayMinimumSize() - framer.GetControlFrameHeaderSize() - 1;
   const unsigned char kV4FrameData[] = {
     0x00, static_cast<uint8>(less_than_min_length), 0x07, 0x00,
     0x00, 0x00, 0x00, 0x00,
@@ -3182,54 +3292,56 @@ TEST_P(SpdyFramerTest, ReadZeroLenSettingsFrame) {
   visitor.SimulateInFramer(
       reinterpret_cast<unsigned char*>(control_frame->data()),
       framer.GetControlFrameHeaderSize());
-  // Should generate an error, since zero-len settings frames are unsupported.
-  EXPECT_EQ(1, visitor.error_count_);
+  if (spdy_version_ <= SPDY3) {
+    // Should generate an error, since zero-len settings frames are unsupported.
+    EXPECT_EQ(1, visitor.error_count_);
+  } else {
+    // Zero-len settings frames are permitted as of SPDY 4.
+    EXPECT_EQ(0, visitor.error_count_);
+  }
 }
 
 // Tests handling of SETTINGS frames with invalid length.
 TEST_P(SpdyFramerTest, ReadBogusLenSettingsFrame) {
   SpdyFramer framer(spdy_version_);
-  SettingsMap settings;
+  SpdySettingsIR settings_ir;
+
   // Add a setting to pad the frame so that we don't get a buffer overflow when
   // calling SimulateInFramer() below.
-  settings[SETTINGS_UPLOAD_BANDWIDTH] =
-      SettingsFlagsAndValue(SETTINGS_FLAG_PLEASE_PERSIST, 0x00000002);
-  SpdySettingsIR settings_ir;
-  settings_ir.AddSetting(SETTINGS_UPLOAD_BANDWIDTH,
-                         true,  // please persist
+  settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE,
+                         false,
                          false,
                          0x00000002);
   scoped_ptr<SpdyFrame> control_frame(framer.SerializeSettings(settings_ir));
-  const size_t kNewLength = 5;
+  const size_t kNewLength = 14;
   SetFrameLength(control_frame.get(), kNewLength, spdy_version_);
   TestSpdyVisitor visitor(spdy_version_);
   visitor.use_compression_ = false;
   visitor.SimulateInFramer(
       reinterpret_cast<unsigned char*>(control_frame->data()),
       framer.GetControlFrameHeaderSize() + kNewLength);
-  // Should generate an error, since zero-len settings frames are unsupported.
+  // Should generate an error, since its not possible to have a
+  // settings frame of length kNewLength.
   EXPECT_EQ(1, visitor.error_count_);
 }
 
 // Tests handling of SETTINGS frames larger than the frame buffer size.
 TEST_P(SpdyFramerTest, ReadLargeSettingsFrame) {
   SpdyFramer framer(spdy_version_);
-  SettingsMap settings;
-  SpdySettingsFlags flags = SETTINGS_FLAG_PLEASE_PERSIST;
-  settings[SETTINGS_UPLOAD_BANDWIDTH] =
-      SettingsFlagsAndValue(flags, 0x00000002);
-  settings[SETTINGS_DOWNLOAD_BANDWIDTH] =
-      SettingsFlagsAndValue(flags, 0x00000003);
-  settings[SETTINGS_ROUND_TRIP_TIME] = SettingsFlagsAndValue(flags, 0x00000004);
   SpdySettingsIR settings_ir;
-  for (SettingsMap::const_iterator it = settings.begin();
-       it != settings.end();
-       ++it) {
-    settings_ir.AddSetting(it->first,
-                           it->second.first & SETTINGS_FLAG_PLEASE_PERSIST,
-                           it->second.first & SETTINGS_FLAG_PERSISTED,
-                           it->second.second);
-  }
+  settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 1),
+                         false,  // persist
+                         false,  // persisted
+                         5);
+  settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 2),
+                         false,  // persist
+                         false,  // persisted
+                         6);
+  settings_ir.AddSetting(SpdyConstants::ParseSettingId(spdy_version_, 3),
+                         false,  // persist
+                         false,  // persisted
+                         7);
+
   scoped_ptr<SpdyFrame> control_frame(framer.SerializeSettings(settings_ir));
   EXPECT_LT(SpdyFramer::kControlFrameBufferSize,
             control_frame->size());
@@ -3241,7 +3353,10 @@ TEST_P(SpdyFramerTest, ReadLargeSettingsFrame) {
       reinterpret_cast<unsigned char*>(control_frame->data()),
       control_frame->size());
   EXPECT_EQ(0, visitor.error_count_);
-  EXPECT_EQ(settings.size(), static_cast<unsigned>(visitor.setting_count_));
+  EXPECT_EQ(3, visitor.setting_count_);
+  if (spdy_version_ > SPDY3) {
+    EXPECT_EQ(1, visitor.settings_ack_sent_);
+  }
 
   // Read data in small chunks.
   size_t framed_data = 0;
@@ -3256,7 +3371,10 @@ TEST_P(SpdyFramerTest, ReadLargeSettingsFrame) {
     framed_data += to_read;
   }
   EXPECT_EQ(0, visitor.error_count_);
-  EXPECT_EQ(settings.size() * 2, static_cast<unsigned>(visitor.setting_count_));
+  EXPECT_EQ(3 * 2, visitor.setting_count_);
+  if (spdy_version_ > SPDY3) {
+    EXPECT_EQ(2, visitor.settings_ack_sent_);
+  }
 }
 
 // Tests handling of SETTINGS frame with duplicate entries.
@@ -3286,14 +3404,13 @@ TEST_P(SpdyFramerTest, ReadDuplicateSettings) {
     0x00, 0x00, 0x00, 0x03,
   };
   const unsigned char kV4FrameData[] = {
-    0x00, 0x24, 0x04, 0x00,
+    0x00, 0x0f, 0x04, 0x00,
     0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x03,
-    0x00, 0x00, 0x00, 0x01,  // 1st Setting
+    0x01,  // 1st Setting
     0x00, 0x00, 0x00, 0x02,
-    0x00, 0x00, 0x00, 0x01,  // 2nd (duplicate) Setting
+    0x01,  // 2nd (duplicate) Setting
     0x00, 0x00, 0x00, 0x03,
-    0x00, 0x00, 0x00, 0x03,  // 3rd (unprocessed) Setting
+    0x03,  // 3rd (unprocessed) Setting
     0x00, 0x00, 0x00, 0x03,
   };
 
@@ -3306,8 +3423,17 @@ TEST_P(SpdyFramerTest, ReadDuplicateSettings) {
   } else {
     visitor.SimulateInFramer(kV4FrameData, sizeof(kV4FrameData));
   }
-  EXPECT_EQ(1, visitor.error_count_);
-  EXPECT_EQ(1, visitor.setting_count_);
+
+  if (!IsSpdy4()) {
+    EXPECT_EQ(1, visitor.setting_count_);
+    EXPECT_EQ(1, visitor.error_count_);
+  } else {
+    // In SPDY 4+, duplicate settings are allowed;
+    // each setting replaces the previous value for that setting.
+    EXPECT_EQ(3, visitor.setting_count_);
+    EXPECT_EQ(0, visitor.error_count_);
+    EXPECT_EQ(1, visitor.settings_ack_sent_);
+  }
 }
 
 // Tests handling of SETTINGS frame with entries out of order.
@@ -3337,14 +3463,13 @@ TEST_P(SpdyFramerTest, ReadOutOfOrderSettings) {
     0x00, 0x00, 0x00, 0x03,
   };
   const unsigned char kV4FrameData[] = {
-    0x00, 0x24, 0x04, 0x00,
+    0x00, 0x0f, 0x04, 0x00,
     0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x03,
-    0x00, 0x00, 0x00, 0x02,  // 1st Setting
+    0x02,  // 1st Setting
     0x00, 0x00, 0x00, 0x02,
-    0x00, 0x00, 0x00, 0x01,  // 2nd (out of order) Setting
+    0x01,  // 2nd (out of order) Setting
     0x00, 0x00, 0x00, 0x03,
-    0x00, 0x00, 0x01, 0x03,  // 3rd (unprocessed) Setting
+    0x03,  // 3rd (unprocessed) Setting
     0x00, 0x00, 0x00, 0x03,
   };
 
@@ -3357,14 +3482,111 @@ TEST_P(SpdyFramerTest, ReadOutOfOrderSettings) {
   } else {
     visitor.SimulateInFramer(kV4FrameData, sizeof(kV4FrameData));
   }
-  EXPECT_EQ(1, visitor.error_count_);
-  EXPECT_EQ(1, visitor.setting_count_);
+
+  if (!IsSpdy4()) {
+    EXPECT_EQ(1, visitor.setting_count_);
+    EXPECT_EQ(1, visitor.error_count_);
+  } else {
+    // In SPDY 4+, settings are allowed in any order.
+    EXPECT_EQ(3, visitor.setting_count_);
+    EXPECT_EQ(0, visitor.error_count_);
+    // EXPECT_EQ(1, visitor.settings_ack_count_);
+  }
+}
+
+TEST_P(SpdyFramerTest, ProcessSettingsAckFrame) {
+  if (spdy_version_ <= SPDY3) {
+    return;
+  }
+  SpdyFramer framer(spdy_version_);
+
+  const unsigned char kFrameData[] = {
+    0x00, 0x00, 0x04, 0x01,
+    0x00, 0x00, 0x00, 0x00,
+  };
+
+  TestSpdyVisitor visitor(spdy_version_);
+  visitor.use_compression_ = false;
+  visitor.SimulateInFramer(kFrameData, sizeof(kFrameData));
+
+  EXPECT_EQ(0, visitor.error_count_);
+  EXPECT_EQ(0, visitor.setting_count_);
+  EXPECT_EQ(1, visitor.settings_ack_received_);
+}
+
+
+TEST_P(SpdyFramerTest, ProcessDataFrameWithPadding) {
+  if (spdy_version_ <= SPDY3) {
+    return;
+  }
+
+  const int kPaddingLen = 512;  // So we get two bytes for padding length field.
+  const char data_payload[] = "hello";
+
+  testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
+  SpdyFramer framer(spdy_version_);
+  framer.set_visitor(&visitor);
+
+  SpdyDataIR data_ir(1, StringPiece(data_payload, strlen(data_payload)));
+  data_ir.set_padding_len(kPaddingLen);
+  scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
+  ASSERT_TRUE(frame.get() != NULL);
+
+  int bytes_consumed = 0;
+
+  // Send the frame header.
+  EXPECT_CALL(visitor, OnDataFrameHeader(1,
+                                         kPaddingLen + strlen(data_payload),
+                                         false));
+  CHECK_EQ(8u, framer.ProcessInput(frame->data(), 8));
+  CHECK_EQ(framer.state(), SpdyFramer::SPDY_READ_PADDING_LENGTH);
+  CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
+  bytes_consumed += 8;
+
+  // Send the first byte of the padding length field.
+  CHECK_EQ(1u, framer.ProcessInput(frame->data() + bytes_consumed, 1));
+  CHECK_EQ(framer.state(), SpdyFramer::SPDY_READ_PADDING_LENGTH);
+  CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
+  bytes_consumed += 1;
+
+  // Send the second byte of the padding length field.
+  CHECK_EQ(1u, framer.ProcessInput(frame->data() + bytes_consumed, 1));
+  CHECK_EQ(framer.state(), SpdyFramer::SPDY_FORWARD_STREAM_FRAME);
+  CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
+  bytes_consumed += 1;
+
+  // Send the first two bytes of the data payload.
+  EXPECT_CALL(visitor, OnStreamFrameData(1, _, 2, false));
+  CHECK_EQ(2u, framer.ProcessInput(frame->data() + bytes_consumed, 2));
+  CHECK_EQ(framer.state(), SpdyFramer::SPDY_FORWARD_STREAM_FRAME);
+  CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
+  bytes_consumed += 2;
+
+  // Send the rest three bytes of the data payload.
+  EXPECT_CALL(visitor, OnStreamFrameData(1, _, 3, false));
+  CHECK_EQ(3u, framer.ProcessInput(frame->data() + bytes_consumed, 3));
+  CHECK_EQ(framer.state(), SpdyFramer::SPDY_CONSUME_PADDING);
+  CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
+  bytes_consumed += 3;
+
+  // Send the first 100 bytes of the padding payload.
+  EXPECT_CALL(visitor, OnStreamFrameData(1, NULL, 100, false));
+  CHECK_EQ(100u, framer.ProcessInput(frame->data() + bytes_consumed, 100));
+  CHECK_EQ(framer.state(), SpdyFramer::SPDY_CONSUME_PADDING);
+  CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
+  bytes_consumed += 100;
+
+  // Send rest of the padding payload.
+  EXPECT_CALL(visitor, OnStreamFrameData(1, NULL, 410, false));
+  CHECK_EQ(410u, framer.ProcessInput(frame->data() + bytes_consumed, 410));
+  CHECK_EQ(framer.state(), SpdyFramer::SPDY_RESET);
+  CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
 }
 
 TEST_P(SpdyFramerTest, ReadWindowUpdate) {
   SpdyFramer framer(spdy_version_);
   scoped_ptr<SpdyFrame> control_frame(
-      framer.SerializeWindowUpdate(net::SpdyWindowUpdateIR(1, 2)));
+      framer.SerializeWindowUpdate(SpdyWindowUpdateIR(1, 2)));
   TestSpdyVisitor visitor(spdy_version_);
   visitor.SimulateInFramer(
       reinterpret_cast<unsigned char*>(control_frame->data()),
@@ -3428,7 +3650,7 @@ TEST_P(SpdyFramerTest, ReadCredentialFrameFollowedByAnotherFrame) {
   string multiple_frame_data(reinterpret_cast<const char*>(kV3FrameData),
                              arraysize(kV3FrameData));
   scoped_ptr<SpdyFrame> control_frame(
-      framer.SerializeWindowUpdate(net::SpdyWindowUpdateIR(1, 2)));
+      framer.SerializeWindowUpdate(SpdyWindowUpdateIR(1, 2)));
   multiple_frame_data.append(string(control_frame->data(),
                                     control_frame->size()));
   visitor.SimulateInFramer(
@@ -3439,8 +3661,35 @@ TEST_P(SpdyFramerTest, ReadCredentialFrameFollowedByAnotherFrame) {
   EXPECT_EQ(2u, visitor.last_window_update_delta_);
 }
 
+TEST_P(SpdyFramerTest, CreateContinuationUncompressed) {
+  if (spdy_version_ < SPDY4) {
+    return;
+  }
+
+  SpdyFramer framer(spdy_version_);
+  framer.set_enable_compression(false);
+  const char kDescription[] = "CONTINUATION frame";
+
+  const unsigned char kFrameData[] = {
+     0x00, 0x12, 0x09, 0x00,  // CONTINUATION
+     0x00, 0x00, 0x00, 0x2a,  // Stream 42
+     0x40, 0x03, 0x62, 0x61,  // @.ba
+     0x72, 0x03, 0x66, 0x6f,  // r.fo
+     0x6f, 0x40, 0x03, 0x66,  // o@.f
+     0x6f, 0x6f, 0x03, 0x62,  // oo.b
+     0x61, 0x72,              // ar
+  };
+
+  SpdyContinuationIR continuation(42);
+  continuation.SetHeader("bar", "foo");
+  continuation.SetHeader("foo", "bar");
+  scoped_ptr<SpdySerializedFrame> frame(
+    framer.SerializeContinuation(continuation));
+  CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+}
+
 TEST_P(SpdyFramerTest, ReadCompressedPushPromise) {
-  if (spdy_version_ < 4) {
+  if (spdy_version_ <= SPDY3) {
     return;
   }
 
@@ -3462,6 +3711,277 @@ TEST_P(SpdyFramerTest, ReadCompressedPushPromise) {
   EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
 }
 
+TEST_P(SpdyFramerTest, ReadHeadersWithContinuation) {
+  if (spdy_version_ <= SPDY3) {
+    return;
+  }
+
+  const unsigned char kInput[] = {
+    0x00, 0x10, 0x01, 0x00,  // HEADERS
+    0x00, 0x00, 0x00, 0x01,  // Stream 1
+    0x40, 0x06, 0x43, 0x6f,
+    0x6f, 0x6b, 0x69, 0x65,
+    0x07, 0x66, 0x6f, 0x6f,
+    0x3d, 0x62, 0x61, 0x72,
+
+    0x00, 0x14, 0x09, 0x00,  // CONTINUATION
+    0x00, 0x00, 0x00, 0x01,  // Stream 1
+    0x40, 0x06, 0x43, 0x6f,
+    0x6f, 0x6b, 0x69, 0x65,
+    0x08, 0x62, 0x61, 0x7a,
+    0x3d, 0x62, 0x69, 0x6e,
+    0x67, 0x40, 0x06, 0x43,
+
+    0x00, 0x12, 0x09, 0x04,  // CONTINUATION: END_HEADERS
+    0x00, 0x00, 0x00, 0x01,  // Stream 1
+    0x6f, 0x6f, 0x6b, 0x69,
+    0x65, 0x00, 0x40, 0x04,
+    0x6e, 0x61, 0x6d, 0x65,
+    0x05, 0x76, 0x61, 0x6c,
+    0x75, 0x65,
+  };
+
+  SpdyFramer framer(spdy_version_);
+  TestSpdyVisitor visitor(spdy_version_);
+  visitor.SimulateInFramer(kInput, sizeof(kInput));
+
+  EXPECT_EQ(0, visitor.error_count_);
+  EXPECT_EQ(1, visitor.headers_frame_count_);
+  EXPECT_EQ(2, visitor.continuation_count_);
+  EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_);
+  EXPECT_EQ(0, visitor.zero_length_data_frame_count_);
+
+  EXPECT_THAT(visitor.headers_, ElementsAre(
+      Pair("Cookie", "foo=bar; baz=bing; "),
+      Pair("name", "value")));
+}
+
+TEST_P(SpdyFramerTest, ReadHeadersWithContinuationAndFin) {
+  if (spdy_version_ <= SPDY3) {
+    return;
+  }
+
+  const unsigned char kInput[] = {
+    0x00, 0x10, 0x01, 0x01,  // HEADERS: FIN
+    0x00, 0x00, 0x00, 0x01,  // Stream 1
+    0x40, 0x06, 0x43, 0x6f,
+    0x6f, 0x6b, 0x69, 0x65,
+    0x07, 0x66, 0x6f, 0x6f,
+    0x3d, 0x62, 0x61, 0x72,
+
+    0x00, 0x14, 0x09, 0x00,  // CONTINUATION
+    0x00, 0x00, 0x00, 0x01,  // Stream 1
+    0x40, 0x06, 0x43, 0x6f,
+    0x6f, 0x6b, 0x69, 0x65,
+    0x08, 0x62, 0x61, 0x7a,
+    0x3d, 0x62, 0x69, 0x6e,
+    0x67, 0x40, 0x06, 0x43,
+
+    0x00, 0x12, 0x09, 0x04,  // CONTINUATION: END_HEADERS
+    0x00, 0x00, 0x00, 0x01,  // Stream 1
+    0x6f, 0x6f, 0x6b, 0x69,
+    0x65, 0x00, 0x40, 0x04,
+    0x6e, 0x61, 0x6d, 0x65,
+    0x05, 0x76, 0x61, 0x6c,
+    0x75, 0x65,
+  };
+
+  SpdyFramer framer(spdy_version_);
+  TestSpdyVisitor visitor(spdy_version_);
+  visitor.SimulateInFramer(kInput, sizeof(kInput));
+
+  EXPECT_EQ(0, visitor.error_count_);
+  EXPECT_EQ(1, visitor.headers_frame_count_);
+  EXPECT_EQ(2, visitor.continuation_count_);
+  EXPECT_EQ(1, visitor.fin_flag_count_);
+  EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_);
+  EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
+
+  EXPECT_THAT(visitor.headers_, ElementsAre(
+      Pair("Cookie", "foo=bar; baz=bing; "),
+      Pair("name", "value")));
+}
+
+TEST_P(SpdyFramerTest, ReadPushPromiseWithContinuation) {
+  if (spdy_version_ <= SPDY3) {
+    return;
+  }
+
+  const unsigned char kInput[] = {
+    0x00, 0x14, 0x05, 0x00,  // PUSH_PROMISE
+    0x00, 0x00, 0x00, 0x01,  // Stream 1
+    0x00, 0x00, 0x00, 0x2A,  // Promised stream 42
+    0x40, 0x06, 0x43, 0x6f,
+    0x6f, 0x6b, 0x69, 0x65,
+    0x07, 0x66, 0x6f, 0x6f,
+    0x3d, 0x62, 0x61, 0x72,
+
+    0x00, 0x14, 0x09, 0x00,  // CONTINUATION
+    0x00, 0x00, 0x00, 0x01,  // Stream 1
+    0x40, 0x06, 0x43, 0x6f,
+    0x6f, 0x6b, 0x69, 0x65,
+    0x08, 0x62, 0x61, 0x7a,
+    0x3d, 0x62, 0x69, 0x6e,
+    0x67, 0x40, 0x06, 0x43,
+
+    0x00, 0x12, 0x09, 0x04,  // CONTINUATION: END_HEADERS
+    0x00, 0x00, 0x00, 0x01,  // Stream 1
+    0x6f, 0x6f, 0x6b, 0x69,
+    0x65, 0x00, 0x40, 0x04,
+    0x6e, 0x61, 0x6d, 0x65,
+    0x05, 0x76, 0x61, 0x6c,
+    0x75, 0x65,
+  };
+
+  SpdyFramer framer(spdy_version_);
+  TestSpdyVisitor visitor(spdy_version_);
+  visitor.SimulateInFramer(kInput, sizeof(kInput));
+
+  EXPECT_EQ(0, visitor.error_count_);
+  EXPECT_EQ(1u, visitor.last_push_promise_stream_);
+  EXPECT_EQ(42u, visitor.last_push_promise_promised_stream_);
+  EXPECT_EQ(2, visitor.continuation_count_);
+  EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_);
+  EXPECT_EQ(0, visitor.zero_length_data_frame_count_);
+
+  EXPECT_THAT(visitor.headers_, ElementsAre(
+      Pair("Cookie", "foo=bar; baz=bing; "),
+      Pair("name", "value")));
+}
+
+TEST_P(SpdyFramerTest, ReadContinuationWithWrongStreamId) {
+  if (spdy_version_ <= SPDY3) {
+    return;
+  }
+
+  const unsigned char kInput[] = {
+    0x00, 0x10, 0x01, 0x00,  // HEADERS
+    0x00, 0x00, 0x00, 0x01,  // Stream 1
+    0x40, 0x06, 0x43, 0x6f,
+    0x6f, 0x6b, 0x69, 0x65,
+    0x07, 0x66, 0x6f, 0x6f,
+    0x3d, 0x62, 0x61, 0x72,
+
+    0x00, 0x14, 0x09, 0x00,  // CONTINUATION
+    0x00, 0x00, 0x00, 0x02,  // Stream 2
+    0x40, 0x06, 0x43, 0x6f,
+    0x6f, 0x6b, 0x69, 0x65,
+    0x08, 0x62, 0x61, 0x7a,
+    0x3d, 0x62, 0x69, 0x6e,
+    0x67, 0x40, 0x06, 0x43,
+  };
+
+  SpdyFramer framer(spdy_version_);
+  TestSpdyVisitor visitor(spdy_version_);
+  framer.set_visitor(&visitor);
+  visitor.SimulateInFramer(kInput, sizeof(kInput));
+
+  EXPECT_EQ(1, visitor.error_count_);
+  EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME,
+            visitor.framer_.error_code())
+      << SpdyFramer::ErrorCodeToString(framer.error_code());
+  EXPECT_EQ(1, visitor.headers_frame_count_);
+  EXPECT_EQ(0, visitor.continuation_count_);
+  EXPECT_EQ(0u, visitor.header_buffer_length_);
+}
+
+TEST_P(SpdyFramerTest, ReadContinuationOutOfOrder) {
+  if (spdy_version_ <= SPDY3) {
+    return;
+  }
+
+  const unsigned char kInput[] = {
+    0x00, 0x10, 0x09, 0x00,  // CONTINUATION
+    0x00, 0x00, 0x00, 0x01,  // Stream 1
+    0x40, 0x06, 0x43, 0x6f,
+    0x6f, 0x6b, 0x69, 0x65,
+    0x07, 0x66, 0x6f, 0x6f,
+    0x3d, 0x62, 0x61, 0x72,
+  };
+
+  SpdyFramer framer(spdy_version_);
+  TestSpdyVisitor visitor(spdy_version_);
+  framer.set_visitor(&visitor);
+  visitor.SimulateInFramer(kInput, sizeof(kInput));
+
+  EXPECT_EQ(1, visitor.error_count_);
+  EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME,
+            visitor.framer_.error_code())
+      << SpdyFramer::ErrorCodeToString(framer.error_code());
+  EXPECT_EQ(0, visitor.continuation_count_);
+  EXPECT_EQ(0u, visitor.header_buffer_length_);
+}
+
+TEST_P(SpdyFramerTest, ExpectContinuationReceiveData) {
+  if (spdy_version_ <= SPDY3) {
+    return;
+  }
+
+  const unsigned char kInput[] = {
+    0x00, 0x10, 0x01, 0x00,  // HEADERS
+    0x00, 0x00, 0x00, 0x01,  // Stream 1
+    0x40, 0x06, 0x43, 0x6f,
+    0x6f, 0x6b, 0x69, 0x65,
+    0x07, 0x66, 0x6f, 0x6f,
+    0x3d, 0x62, 0x61, 0x72,
+
+    0x00, 0x00, 0x00, 0x01,  // DATA on Stream #1
+    0x00, 0x00, 0x00, 0x04,
+    0xde, 0xad, 0xbe, 0xef,
+  };
+
+  SpdyFramer framer(spdy_version_);
+  TestSpdyVisitor visitor(spdy_version_);
+  framer.set_visitor(&visitor);
+  visitor.SimulateInFramer(kInput, sizeof(kInput));
+
+  EXPECT_EQ(1, visitor.error_count_);
+  EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME,
+            visitor.framer_.error_code())
+      << SpdyFramer::ErrorCodeToString(framer.error_code());
+  EXPECT_EQ(1, visitor.headers_frame_count_);
+  EXPECT_EQ(0, visitor.continuation_count_);
+  EXPECT_EQ(0u, visitor.header_buffer_length_);
+  EXPECT_EQ(0, visitor.data_frame_count_);
+}
+
+TEST_P(SpdyFramerTest, ExpectContinuationReceiveControlFrame) {
+  if (spdy_version_ <= SPDY3) {
+    return;
+  }
+
+  const unsigned char kInput[] = {
+    0x00, 0x10, 0x01, 0x00,  // HEADERS
+    0x00, 0x00, 0x00, 0x01,  // Stream 1
+    0x40, 0x06, 0x43, 0x6f,
+    0x6f, 0x6b, 0x69, 0x65,
+    0x07, 0x66, 0x6f, 0x6f,
+    0x3d, 0x62, 0x61, 0x72,
+
+    0x00, 0x14, 0x08, 0x00,  // HEADERS
+    0x00, 0x00, 0x00, 0x01,  // Stream 1
+    0x40, 0x06, 0x43, 0x6f,  // (Note this is a valid continued encoding).
+    0x6f, 0x6b, 0x69, 0x65,
+    0x08, 0x62, 0x61, 0x7a,
+    0x3d, 0x62, 0x69, 0x6e,
+    0x67, 0x40, 0x06, 0x43,
+  };
+
+  SpdyFramer framer(spdy_version_);
+  TestSpdyVisitor visitor(spdy_version_);
+  framer.set_visitor(&visitor);
+  visitor.SimulateInFramer(kInput, sizeof(kInput));
+
+  EXPECT_EQ(1, visitor.error_count_);
+  EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME,
+            visitor.framer_.error_code())
+      << SpdyFramer::ErrorCodeToString(framer.error_code());
+  EXPECT_EQ(1, visitor.headers_frame_count_);
+  EXPECT_EQ(0, visitor.continuation_count_);
+  EXPECT_EQ(0u, visitor.header_buffer_length_);
+  EXPECT_EQ(0, visitor.data_frame_count_);
+}
+
 TEST_P(SpdyFramerTest, ReadGarbage) {
   SpdyFramer framer(spdy_version_);
   unsigned char garbage_frame[256];
@@ -3472,6 +3992,23 @@ TEST_P(SpdyFramerTest, ReadGarbage) {
   EXPECT_EQ(1, visitor.error_count_);
 }
 
+TEST_P(SpdyFramerTest, ReadGarbageWithValidLength) {
+  if (!IsSpdy4()) {
+    return;
+  }
+  SpdyFramer framer(spdy_version_);
+  const unsigned char kFrameData[] = {
+    0x00, 0x10, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff,
+  };
+  TestSpdyVisitor visitor(spdy_version_);
+  visitor.use_compression_ = false;
+  visitor.SimulateInFramer(kFrameData, arraysize(kFrameData));
+  EXPECT_EQ(1, visitor.error_count_);
+}
+
 TEST_P(SpdyFramerTest, ReadGarbageWithValidVersion) {
   if (IsSpdy4()) {
     // Not valid for SPDY 4 since there is no version field.
@@ -3494,16 +4031,16 @@ TEST_P(SpdyFramerTest, SizesTest) {
   if (IsSpdy4()) {
     EXPECT_EQ(8u, framer.GetSynReplyMinimumSize());
     EXPECT_EQ(12u, framer.GetRstStreamMinimumSize());
-    EXPECT_EQ(12u, framer.GetSettingsMinimumSize());
-    EXPECT_EQ(12u, framer.GetPingSize());
+    EXPECT_EQ(8u, framer.GetSettingsMinimumSize());
+    EXPECT_EQ(16u, framer.GetPingSize());
     EXPECT_EQ(16u, framer.GetGoAwayMinimumSize());
     EXPECT_EQ(8u, framer.GetHeadersMinimumSize());
     EXPECT_EQ(12u, framer.GetWindowUpdateSize());
     EXPECT_EQ(8u, framer.GetBlockedSize());
     EXPECT_EQ(12u, framer.GetPushPromiseMinimumSize());
     EXPECT_EQ(8u, framer.GetFrameMinimumSize());
-    EXPECT_EQ(65535u, framer.GetFrameMaximumSize());
-    EXPECT_EQ(65527u, framer.GetDataFrameMaximumPayload());
+    EXPECT_EQ(16383u, framer.GetFrameMaximumSize());
+    EXPECT_EQ(16375u, framer.GetDataFrameMaximumPayload());
   } else {
     EXPECT_EQ(8u, framer.GetControlFrameHeaderSize());
     EXPECT_EQ(18u, framer.GetSynStreamMinimumSize());
@@ -3515,8 +4052,8 @@ TEST_P(SpdyFramerTest, SizesTest) {
     EXPECT_EQ(IsSpdy2() ? 14u : 12u, framer.GetHeadersMinimumSize());
     EXPECT_EQ(16u, framer.GetWindowUpdateSize());
     EXPECT_EQ(8u, framer.GetFrameMinimumSize());
-    EXPECT_EQ(16777215u, framer.GetFrameMaximumSize());
-    EXPECT_EQ(16777207u, framer.GetDataFrameMaximumPayload());
+    EXPECT_EQ(16777223u, framer.GetFrameMaximumSize());
+    EXPECT_EQ(16777215u, framer.GetDataFrameMaximumPayload());
   }
 }
 
@@ -3602,7 +4139,7 @@ TEST_P(SpdyFramerTest, StatusCodeToStringTest) {
   EXPECT_STREQ("FLOW_CONTROL_ERROR",
                SpdyFramer::StatusCodeToString(RST_STREAM_FLOW_CONTROL_ERROR));
   EXPECT_STREQ("UNKNOWN_STATUS",
-               SpdyFramer::StatusCodeToString(RST_STREAM_NUM_STATUS_CODES));
+               SpdyFramer::StatusCodeToString(-1));
 }
 
 TEST_P(SpdyFramerTest, FrameTypeToStringTest) {
@@ -3630,6 +4167,8 @@ TEST_P(SpdyFramerTest, FrameTypeToStringTest) {
                SpdyFramer::FrameTypeToString(PUSH_PROMISE));
   EXPECT_STREQ("CREDENTIAL",
                SpdyFramer::FrameTypeToString(CREDENTIAL));
+  EXPECT_STREQ("CONTINUATION",
+               SpdyFramer::FrameTypeToString(CONTINUATION));
 }
 
 TEST_P(SpdyFramerTest, CatchProbableHttpResponse) {
@@ -3663,7 +4202,11 @@ TEST_P(SpdyFramerTest, CatchProbableHttpResponse) {
   }
 }
 
-TEST_P(SpdyFramerTest, DataFrameFlags) {
+TEST_P(SpdyFramerTest, DataFrameFlagsV2V3) {
+  if (spdy_version_ > SPDY3) {
+    return;
+  }
+
   for (int flags = 0; flags < 256; ++flags) {
     SCOPED_TRACE(testing::Message() << "Flags " << flags);
 
@@ -3671,7 +4214,7 @@ TEST_P(SpdyFramerTest, DataFrameFlags) {
     SpdyFramer framer(spdy_version_);
     framer.set_visitor(&visitor);
 
-    net::SpdyDataIR data_ir(1, StringPiece("hello", 5));
+    SpdyDataIR data_ir(1, StringPiece("hello", 5));
     scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
     SetFrameFlags(frame.get(), flags, spdy_version_);
 
@@ -3699,6 +4242,55 @@ TEST_P(SpdyFramerTest, DataFrameFlags) {
   }
 }
 
+TEST_P(SpdyFramerTest, DataFrameFlagsV4) {
+  if (spdy_version_ <= SPDY3) {
+    return;
+  }
+
+  uint8 valid_data_flags = DATA_FLAG_FIN | DATA_FLAG_END_SEGMENT |
+      DATA_FLAG_PAD_LOW | DATA_FLAG_PAD_HIGH;
+
+  for (int flags = 0; flags < 256; ++flags) {
+    SCOPED_TRACE(testing::Message() << "Flags " << flags);
+
+    testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
+    SpdyFramer framer(spdy_version_);
+    framer.set_visitor(&visitor);
+
+    SpdyDataIR data_ir(1, StringPiece("hello", 5));
+    scoped_ptr<SpdyFrame> frame(framer.SerializeData(data_ir));
+    SetFrameFlags(frame.get(), flags, spdy_version_);
+
+    if (flags & ~valid_data_flags) {
+      EXPECT_CALL(visitor, OnError(_));
+    } else {
+      EXPECT_CALL(visitor, OnDataFrameHeader(1, 5, flags & DATA_FLAG_FIN));
+      if ((flags & DATA_FLAG_PAD_LOW) || (flags & DATA_FLAG_PAD_HIGH)) {
+        // Expect Error since we don't set pad_high and pad_low in payload.
+        EXPECT_CALL(visitor, OnError(_));
+      } else {
+        EXPECT_CALL(visitor, OnStreamFrameData(_, _, 5, false));
+        if (flags & DATA_FLAG_FIN) {
+          EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true));
+        }
+      }
+    }
+
+    framer.ProcessInput(frame->data(), frame->size());
+    if ((flags & ~valid_data_flags) || (flags & DATA_FLAG_PAD_LOW) ||
+        (flags & DATA_FLAG_PAD_HIGH)) {
+        EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
+        EXPECT_EQ(SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS,
+                  framer.error_code())
+            << SpdyFramer::ErrorCodeToString(framer.error_code());
+    } else {
+      EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
+      EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
+          << SpdyFramer::ErrorCodeToString(framer.error_code());
+    }
+  }
+}
+
 TEST_P(SpdyFramerTest, SynStreamFrameFlags) {
   for (int flags = 0; flags < 256; ++flags) {
     SCOPED_TRACE(testing::Message() << "Flags " << flags);
@@ -3727,21 +4319,27 @@ TEST_P(SpdyFramerTest, SynStreamFrameFlags) {
         flags & ~(CONTROL_FLAG_FIN | CONTROL_FLAG_UNIDIRECTIONAL)) {
       EXPECT_CALL(visitor, OnError(_));
     } else if (IsSpdy4() &&
-               flags & ~(CONTROL_FLAG_FIN | HEADERS_FLAG_PRIORITY)) {
+               flags & ~(CONTROL_FLAG_FIN |
+                         HEADERS_FLAG_PRIORITY |
+                         HEADERS_FLAG_END_HEADERS)) {
       EXPECT_CALL(visitor, OnError(_));
     } else {
       EXPECT_CALL(debug_visitor, OnReceiveCompressedFrame(8, SYN_STREAM, _));
       if (IsSpdy4()) {
-        EXPECT_CALL(visitor, OnSynStream(8, 0, 1, 0, flags & CONTROL_FLAG_FIN,
+        EXPECT_CALL(visitor, OnSynStream(8, 0, 1, flags & CONTROL_FLAG_FIN,
                                          false));
       } else {
-        EXPECT_CALL(visitor, OnSynStream(8, 3, 1, 0, flags & CONTROL_FLAG_FIN,
+        EXPECT_CALL(visitor, OnSynStream(8, 3, 1, flags & CONTROL_FLAG_FIN,
                                          flags & CONTROL_FLAG_UNIDIRECTIONAL));
       }
       EXPECT_CALL(visitor, OnControlFrameHeaderData(8, _, _))
           .WillRepeatedly(testing::Return(true));
-      if (flags & DATA_FLAG_FIN) {
+      if (flags & DATA_FLAG_FIN && (!IsSpdy4() ||
+                                    flags & HEADERS_FLAG_END_HEADERS)) {
         EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true));
+      } else {
+        // Do not close the stream if we are expecting a CONTINUATION frame.
+        EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true)).Times(0);
       }
     }
 
@@ -3753,7 +4351,9 @@ TEST_P(SpdyFramerTest, SynStreamFrameFlags) {
                 framer.error_code())
           << SpdyFramer::ErrorCodeToString(framer.error_code());
     } else if (IsSpdy4() &&
-        flags & ~(CONTROL_FLAG_FIN | HEADERS_FLAG_PRIORITY)) {
+        flags & ~(CONTROL_FLAG_FIN |
+                  HEADERS_FLAG_PRIORITY |
+                  HEADERS_FLAG_END_HEADERS)) {
       EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
       EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
                 framer.error_code())
@@ -3816,7 +4416,7 @@ TEST_P(SpdyFramerTest, RstStreamFrameFlags) {
     SpdyFramer framer(spdy_version_);
     framer.set_visitor(&visitor);
 
-    net::SpdyRstStreamIR rst_stream(13, RST_STREAM_CANCEL, "");
+    SpdyRstStreamIR rst_stream(13, RST_STREAM_CANCEL, "");
     scoped_ptr<SpdyFrame> frame(framer.SerializeRstStream(rst_stream));
     SetFrameFlags(frame.get(), flags, spdy_version_);
 
@@ -3840,7 +4440,8 @@ TEST_P(SpdyFramerTest, RstStreamFrameFlags) {
   }
 }
 
-TEST_P(SpdyFramerTest, SettingsFrameFlags) {
+TEST_P(SpdyFramerTest, SettingsFrameFlagsOldFormat) {
+  if (spdy_version_ > SPDY3) { return; }
   for (int flags = 0; flags < 256; ++flags) {
     SCOPED_TRACE(testing::Message() << "Flags " << flags);
 
@@ -3863,6 +4464,7 @@ TEST_P(SpdyFramerTest, SettingsFrameFlags) {
           flags & SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS));
       EXPECT_CALL(visitor, OnSetting(SETTINGS_UPLOAD_BANDWIDTH,
                                      SETTINGS_FLAG_NONE, 54321));
+      EXPECT_CALL(visitor, OnSettingsEnd());
     }
 
     framer.ProcessInput(frame->data(), frame->size());
@@ -3879,6 +4481,48 @@ TEST_P(SpdyFramerTest, SettingsFrameFlags) {
   }
 }
 
+TEST_P(SpdyFramerTest, SettingsFrameFlags) {
+  if (spdy_version_ <= SPDY3) { return; }
+  for (int flags = 0; flags < 256; ++flags) {
+    SCOPED_TRACE(testing::Message() << "Flags " << flags);
+
+    testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
+    SpdyFramer framer(spdy_version_);
+    framer.set_visitor(&visitor);
+
+    SpdySettingsIR settings_ir;
+    settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 0, 0, 16);
+    scoped_ptr<SpdyFrame> frame(framer.SerializeSettings(settings_ir));
+    SetFrameFlags(frame.get(), flags, spdy_version_);
+
+    if (flags != 0) {
+      EXPECT_CALL(visitor, OnError(_));
+    } else {
+      EXPECT_CALL(visitor, OnSettings(flags & SETTINGS_FLAG_ACK));
+      EXPECT_CALL(visitor, OnSetting(SETTINGS_INITIAL_WINDOW_SIZE, 0, 16));
+      EXPECT_CALL(visitor, OnSettingsEnd());
+    }
+
+    framer.ProcessInput(frame->data(), frame->size());
+    if (flags & ~SETTINGS_FLAG_ACK) {
+      EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
+      EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
+                framer.error_code())
+          << SpdyFramer::ErrorCodeToString(framer.error_code());
+    } else if (flags & SETTINGS_FLAG_ACK) {
+      // The frame is invalid because ACK frames should have no payload.
+      EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
+      EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME,
+                framer.error_code())
+          << SpdyFramer::ErrorCodeToString(framer.error_code());
+    } else {
+      EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
+      EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
+          << SpdyFramer::ErrorCodeToString(framer.error_code());
+    }
+  }
+}
+
 TEST_P(SpdyFramerTest, GoawayFrameFlags) {
   for (int flags = 0; flags < 256; ++flags) {
     SCOPED_TRACE(testing::Message() << "Flags " << flags);
@@ -3928,23 +4572,43 @@ TEST_P(SpdyFramerTest, HeadersFrameFlags) {
     scoped_ptr<SpdyFrame> frame(framer.SerializeHeaders(headers_ir));
     SetFrameFlags(frame.get(), flags, spdy_version_);
 
-    if (flags & ~CONTROL_FLAG_FIN) {
+    if (!IsSpdy4() && flags & ~CONTROL_FLAG_FIN) {
+      EXPECT_CALL(visitor, OnError(_));
+    } else if (IsSpdy4() && flags & ~(CONTROL_FLAG_FIN |
+                                      HEADERS_FLAG_END_HEADERS)) {
       EXPECT_CALL(visitor, OnError(_));
     } else {
-      EXPECT_CALL(visitor, OnHeaders(57, flags & CONTROL_FLAG_FIN));
+      EXPECT_CALL(visitor, OnHeaders(57,
+                                     flags & CONTROL_FLAG_FIN,
+                                     (flags & HEADERS_FLAG_END_HEADERS) ||
+                                      !IsSpdy4()));
       EXPECT_CALL(visitor, OnControlFrameHeaderData(57, _, _))
           .WillRepeatedly(testing::Return(true));
-      if (flags & DATA_FLAG_FIN) {
+      if (flags & DATA_FLAG_FIN  && (!IsSpdy4() ||
+                                     flags & HEADERS_FLAG_END_HEADERS)) {
         EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true));
+      } else {
+        // Do not close the stream if we are expecting a CONTINUATION frame.
+        EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0, true)).Times(0);
       }
     }
 
     framer.ProcessInput(frame->data(), frame->size());
-    if (flags & ~CONTROL_FLAG_FIN) {
+    if (!IsSpdy4() && flags & ~CONTROL_FLAG_FIN) {
+      EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
+      EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
+                framer.error_code())
+          << SpdyFramer::ErrorCodeToString(framer.error_code());
+    } else if (IsSpdy4() && flags & ~(CONTROL_FLAG_FIN | HEADERS_FLAG_PRIORITY |
+                                      HEADERS_FLAG_END_HEADERS)) {
       EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
       EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
                 framer.error_code())
           << SpdyFramer::ErrorCodeToString(framer.error_code());
+    } else if (IsSpdy4() && ~(flags & HEADERS_FLAG_END_HEADERS)) {
+      EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
+      EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
+          << SpdyFramer::ErrorCodeToString(framer.error_code());
     } else {
       EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
       EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
@@ -3964,22 +4628,26 @@ TEST_P(SpdyFramerTest, PingFrameFlags) {
     scoped_ptr<SpdyFrame> frame(framer.SerializePing(SpdyPingIR(42)));
     SetFrameFlags(frame.get(), flags, spdy_version_);
 
-    if (flags != 0) {
-      EXPECT_CALL(visitor, OnError(_));
+    if (spdy_version_ > SPDY3 &&
+        flags == PING_FLAG_ACK) {
+      EXPECT_CALL(visitor, OnPing(42, true));
+    } else if (flags == 0) {
+      EXPECT_CALL(visitor, OnPing(42, false));
     } else {
-      EXPECT_CALL(visitor, OnPing(42));
+      EXPECT_CALL(visitor, OnError(_));
     }
 
     framer.ProcessInput(frame->data(), frame->size());
-    if (flags != 0) {
+    if ((spdy_version_ > SPDY3 && flags == PING_FLAG_ACK) ||
+        flags == 0) {
+      EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
+      EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
+          << SpdyFramer::ErrorCodeToString(framer.error_code());
+    } else {
       EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
       EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
                 framer.error_code())
           << SpdyFramer::ErrorCodeToString(framer.error_code());
-    } else {
-      EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
-      EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
-          << SpdyFramer::ErrorCodeToString(framer.error_code());
     }
   }
 }
@@ -3993,7 +4661,7 @@ TEST_P(SpdyFramerTest, WindowUpdateFrameFlags) {
     framer.set_visitor(&visitor);
 
     scoped_ptr<SpdyFrame> frame(framer.SerializeWindowUpdate(
-        net::SpdyWindowUpdateIR(4, 1024)));
+        SpdyWindowUpdateIR(4, 1024)));
     SetFrameFlags(frame.get(), flags, spdy_version_);
 
     if (flags != 0) {
@@ -4038,17 +4706,75 @@ TEST_P(SpdyFramerTest, PushPromiseFrameFlags) {
     framer.SerializePushPromise(push_promise));
     SetFrameFlags(frame.get(), flags, spdy_version_);
 
-    if (flags != 0) {
+    if (flags & ~(PUSH_PROMISE_FLAG_END_PUSH_PROMISE)) {
       EXPECT_CALL(visitor, OnError(_));
     } else {
       EXPECT_CALL(debug_visitor, OnReceiveCompressedFrame(42, PUSH_PROMISE, _));
-      EXPECT_CALL(visitor, OnPushPromise(42, 57));
+      EXPECT_CALL(visitor, OnPushPromise(42, 57,
+          flags & PUSH_PROMISE_FLAG_END_PUSH_PROMISE));
       EXPECT_CALL(visitor, OnControlFrameHeaderData(42, _, _))
           .WillRepeatedly(testing::Return(true));
     }
 
     framer.ProcessInput(frame->data(), frame->size());
-    if (flags != 0) {
+    if (flags & ~(PUSH_PROMISE_FLAG_END_PUSH_PROMISE)) {
+      EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
+      EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
+                framer.error_code())
+          << SpdyFramer::ErrorCodeToString(framer.error_code());
+    } else {
+      EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
+      EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
+          << SpdyFramer::ErrorCodeToString(framer.error_code());
+    }
+  }
+}
+
+TEST_P(SpdyFramerTest, ContinuationFrameFlags) {
+  if (spdy_version_ < SPDY4) {
+    return;
+  }
+
+  for (int flags = 0; flags < 256; ++flags) {
+    SCOPED_TRACE(testing::Message() << "Flags " << flags);
+
+    testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
+    testing::StrictMock<net::test::MockDebugVisitor> debug_visitor;
+    SpdyFramer framer(spdy_version_);
+    framer.set_visitor(&visitor);
+    framer.set_debug_visitor(&debug_visitor);
+
+    EXPECT_CALL(debug_visitor, OnSendCompressedFrame(42, HEADERS, _, _));
+    EXPECT_CALL(debug_visitor, OnReceiveCompressedFrame(42, HEADERS, _));
+    EXPECT_CALL(visitor, OnHeaders(42, 0, false));
+    EXPECT_CALL(visitor, OnControlFrameHeaderData(42, _, _))
+          .WillRepeatedly(testing::Return(true));
+    EXPECT_CALL(debug_visitor, OnSendCompressedFrame(42, CONTINUATION, _, _));
+
+    SpdyHeadersIR headers_ir(42);
+    headers_ir.SetHeader("foo", "bar");
+    headers_ir.set_end_headers(false);
+    scoped_ptr<SpdyFrame> frame0(framer.SerializeHeaders(headers_ir));
+
+    SpdyContinuationIR continuation(42);
+    continuation.SetHeader("foo", "bar");
+    scoped_ptr<SpdySerializedFrame> frame(
+    framer.SerializeContinuation(continuation));
+    SetFrameFlags(frame.get(), flags, spdy_version_);
+
+    if (flags & ~(HEADERS_FLAG_END_HEADERS)) {
+      EXPECT_CALL(visitor, OnError(_));
+    } else {
+      EXPECT_CALL(debug_visitor, OnReceiveCompressedFrame(42, CONTINUATION, _));
+      EXPECT_CALL(visitor, OnContinuation(42,
+                                          flags & HEADERS_FLAG_END_HEADERS));
+      EXPECT_CALL(visitor, OnControlFrameHeaderData(42, _, _))
+          .WillRepeatedly(testing::Return(true));
+    }
+
+    framer.ProcessInput(frame0->data(), frame0->size());
+    framer.ProcessInput(frame->data(), frame->size());
+    if (flags & ~(HEADERS_FLAG_END_HEADERS)) {
       EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
       EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS,
                 framer.error_code())
@@ -4074,20 +4800,13 @@ TEST_P(SpdyFramerTest, EmptySynStream) {
   syn_stream.set_priority(1);
   scoped_ptr<SpdyFrame> frame(framer.SerializeSynStream(syn_stream));
   // Adjust size to remove the name/value block.
-  if (IsSpdy4()) {
-    SetFrameLength(
-        frame.get(),
-        framer.GetSynStreamMinimumSize(),
-        spdy_version_);
-  } else {
-    SetFrameLength(
-        frame.get(),
-        framer.GetSynStreamMinimumSize() - framer.GetControlFrameHeaderSize(),
-        spdy_version_);
-  }
+  SetFrameLength(
+      frame.get(),
+      framer.GetSynStreamMinimumSize() - framer.GetControlFrameHeaderSize(),
+      spdy_version_);
 
   EXPECT_CALL(debug_visitor, OnReceiveCompressedFrame(1, SYN_STREAM, _));
-  EXPECT_CALL(visitor, OnSynStream(1, 0, 1, 0, false, false));
+  EXPECT_CALL(visitor, OnSynStream(1, 0, 1, false, false));
   EXPECT_CALL(visitor, OnControlFrameHeaderData(1, NULL, 0));
 
   framer.ProcessInput(frame->data(), framer.GetSynStreamMinimumSize());
@@ -4110,61 +4829,72 @@ TEST_P(SpdyFramerTest, SettingsFlagsAndId) {
 
 // Test handling of a RST_STREAM with out-of-bounds status codes.
 TEST_P(SpdyFramerTest, RstStreamStatusBounds) {
-  DCHECK_GE(0xff, RST_STREAM_NUM_STATUS_CODES);
-
+  const unsigned char kRstStreamStatusTooLow = 0x00;
+  const unsigned char kRstStreamStatusTooHigh = 0xff;
   const unsigned char kV3RstStreamInvalid[] = {
     0x80, spdy_version_ch_, 0x00, 0x03,
     0x00, 0x00, 0x00, 0x08,
     0x00, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, RST_STREAM_INVALID
+    0x00, 0x00, 0x00, kRstStreamStatusTooLow
   };
   const unsigned char kV4RstStreamInvalid[] = {
-    0x00, 0x0c, 0x03, 0x00,
+    0x00, 0x04, 0x03, 0x00,
     0x00, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, RST_STREAM_INVALID
+    0x00, 0x00, 0x00, kRstStreamStatusTooLow
   };
 
   const unsigned char kV3RstStreamNumStatusCodes[] = {
     0x80, spdy_version_ch_, 0x00, 0x03,
     0x00, 0x00, 0x00, 0x08,
     0x00, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, RST_STREAM_NUM_STATUS_CODES
+    0x00, 0x00, 0x00, kRstStreamStatusTooHigh
   };
   const unsigned char kV4RstStreamNumStatusCodes[] = {
-    0x00, 0x0c, 0x03, 0x00,
+    0x00, 0x04, 0x03, 0x00,
     0x00, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, RST_STREAM_NUM_STATUS_CODES
+    0x00, 0x00, 0x00, kRstStreamStatusTooHigh
   };
 
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
   SpdyFramer framer(spdy_version_);
   framer.set_visitor(&visitor);
 
-  EXPECT_CALL(visitor, OnRstStream(1, RST_STREAM_INVALID));
   if (IsSpdy4()) {
+    EXPECT_CALL(visitor, OnError(_));
     framer.ProcessInput(reinterpret_cast<const char*>(kV4RstStreamInvalid),
                         arraysize(kV4RstStreamInvalid));
+    EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
+    EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, framer.error_code())
+      << SpdyFramer::ErrorCodeToString(framer.error_code());
   } else {
+    EXPECT_CALL(visitor, OnRstStream(1, RST_STREAM_INVALID));
     framer.ProcessInput(reinterpret_cast<const char*>(kV3RstStreamInvalid),
                         arraysize(kV3RstStreamInvalid));
-  }
-  EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
-  EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
+    EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
+    EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
       << SpdyFramer::ErrorCodeToString(framer.error_code());
+  }
+
+
+  framer.Reset();
 
-  EXPECT_CALL(visitor, OnRstStream(1, RST_STREAM_INVALID));
   if (IsSpdy4()) {
+    EXPECT_CALL(visitor, OnError(_));
     framer.ProcessInput(
         reinterpret_cast<const char*>(kV4RstStreamNumStatusCodes),
         arraysize(kV4RstStreamNumStatusCodes));
+    EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state());
+    EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, framer.error_code())
+      << SpdyFramer::ErrorCodeToString(framer.error_code());
   } else {
+    EXPECT_CALL(visitor, OnRstStream(1, RST_STREAM_INVALID));
     framer.ProcessInput(
         reinterpret_cast<const char*>(kV3RstStreamNumStatusCodes),
         arraysize(kV3RstStreamNumStatusCodes));
-  }
-  EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
-  EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
+    EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state());
+    EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code())
       << SpdyFramer::ErrorCodeToString(framer.error_code());
+  }
 }
 
 // Tests handling of a GOAWAY frame with out-of-bounds stream ID.
@@ -4181,7 +4911,7 @@ TEST_P(SpdyFramerTest, GoAwayStreamIdBounds) {
     0x00, 0x00, 0x00, 0x00,
   };
   const unsigned char kV4FrameData[] = {
-    0x00, 0x10, 0x07, 0x00,
+    0x00, 0x08, 0x07, 0x00,
     0x00, 0x00, 0x00, 0x00,
     0xff, 0xff, 0xff, 0xff,
     0x00, 0x00, 0x00, 0x00,