Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / net / quic / quic_framer_test.cc
index b1ceb49..f9c2de7 100644 (file)
@@ -20,6 +20,7 @@
 #include "net/quic/quic_utils.h"
 #include "net/quic/test_tools/quic_framer_peer.h"
 #include "net/quic/test_tools/quic_test_utils.h"
+#include "net/test/gtest_util.h"
 
 using base::hash_set;
 using base::StringPiece;
@@ -93,11 +94,18 @@ size_t GetFecGroupOffset(bool include_version,
       kPrivateFlagsSize;
 }
 
+// Index into the message tag of the public reset packet.
+// Public resets always have full guids.
+const size_t kPublicResetPacketMessageTagOffset =
+    kGuidOffset + PACKET_8BYTE_GUID;
+
+// TODO(wtc): remove this when we drop support for QUIC_VERSION_13.
 // Index into the nonce proof of the public reset packet.
 // Public resets always have full guids.
 const size_t kPublicResetPacketNonceProofOffset =
     kGuidOffset + PACKET_8BYTE_GUID;
 
+// TODO(wtc): remove this when we drop support for QUIC_VERSION_13.
 // Index into the rejected sequence number of the public reset packet.
 const size_t kPublicResetPacketRejectedSequenceNumberOffset =
     kPublicResetPacketNonceProofOffset + kPublicResetNonceSize;
@@ -300,6 +308,17 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface {
     return true;
   }
 
+  virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame)
+      OVERRIDE {
+    window_update_frame_ = frame;
+    return true;
+  }
+
+  virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE {
+    blocked_frame_ = frame;
+    return true;
+  }
+
   // Counters from the visitor_ callbacks.
   int error_count_;
   int version_mismatch_;
@@ -323,6 +342,8 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface {
   QuicRstStreamFrame rst_stream_frame_;
   QuicConnectionCloseFrame connection_close_frame_;
   QuicGoAwayFrame goaway_frame_;
+  QuicWindowUpdateFrame window_update_frame_;
+  QuicBlockedFrame blocked_frame_;
 };
 
 class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> {
@@ -1636,7 +1657,11 @@ TEST_P(QuicFramerTest, StreamFrameInFecGroup) {
   CheckStreamFrameData("hello world!", visitor_.stream_frames_[0]);
 }
 
-TEST_P(QuicFramerTest, AckFrame) {
+TEST_P(QuicFramerTest, AckFrameV14) {
+  if (framer_.version() > QUIC_VERSION_14) {
+    return;
+  }
+
   unsigned char packet[] = {
     // public flags (8 byte guid)
     0x3C,
@@ -1735,6 +1760,231 @@ TEST_P(QuicFramerTest, AckFrame) {
   }
 }
 
+TEST_P(QuicFramerTest, AckFrame) {
+  if (framer_.version() <= QUIC_VERSION_14) {
+    return;
+  }
+
+  unsigned char packet[] = {
+    // public flags (8 byte guid)
+    0x3C,
+    // guid
+    0x10, 0x32, 0x54, 0x76,
+    0x98, 0xBA, 0xDC, 0xFE,
+    // packet sequence number
+    0xA8, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // private flags (entropy)
+    0x01,
+
+    // frame type (ack frame)
+    // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+    0x6C,
+    // entropy hash of sent packets till least awaiting - 1.
+    0xAB,
+    // least packet sequence number awaiting an ack, delta from sequence number.
+    0x08, 0x00, 0x00, 0x00,
+    0x00, 0x00,
+    // entropy hash of all received packets.
+    0xBA,
+    // largest observed packet sequence number
+    0xBF, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // Zero delta time.
+    0x0, 0x0,
+    // num missing packets
+    0x01,
+    // missing packet delta
+    0x01,
+    // 0 more missing packets in range.
+    0x00,
+    // Number of revived packets.
+    0x00,
+  };
+
+  QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+  EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+  EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+  EXPECT_EQ(0u, visitor_.stream_frames_.size());
+  ASSERT_EQ(1u, visitor_.ack_frames_.size());
+  const QuicAckFrame& frame = *visitor_.ack_frames_[0];
+  EXPECT_EQ(0xAB, frame.sent_info.entropy_hash);
+  EXPECT_EQ(0xBA, frame.received_info.entropy_hash);
+  EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.received_info.largest_observed);
+  ASSERT_EQ(1u, frame.received_info.missing_packets.size());
+  SequenceNumberSet::const_iterator missing_iter =
+      frame.received_info.missing_packets.begin();
+  EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter);
+  EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame.sent_info.least_unacked);
+
+  const size_t kSentEntropyOffset = kQuicFrameTypeSize;
+  const size_t kLeastUnackedOffset = kSentEntropyOffset + kQuicEntropyHashSize;
+  const size_t kReceivedEntropyOffset = kLeastUnackedOffset +
+      PACKET_6BYTE_SEQUENCE_NUMBER;
+  const size_t kLargestObservedOffset = kReceivedEntropyOffset +
+      kQuicEntropyHashSize;
+  const size_t kMissingDeltaTimeOffset = kLargestObservedOffset +
+      PACKET_6BYTE_SEQUENCE_NUMBER;
+  const size_t kNumMissingPacketOffset = kMissingDeltaTimeOffset +
+      kQuicDeltaTimeLargestObservedSize;
+  const size_t kMissingPacketsOffset = kNumMissingPacketOffset +
+      kNumberOfMissingPacketsSize;
+  const size_t kMissingPacketsRange = kMissingPacketsOffset +
+      PACKET_1BYTE_SEQUENCE_NUMBER;
+  const size_t kRevivedPacketsLength = kMissingPacketsRange +
+      PACKET_1BYTE_SEQUENCE_NUMBER;
+  // Now test framing boundaries
+  const size_t ack_frame_size = kRevivedPacketsLength +
+      PACKET_1BYTE_SEQUENCE_NUMBER;
+  for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) {
+    string expected_error;
+    if (i < kLeastUnackedOffset) {
+      expected_error = "Unable to read entropy hash for sent packets.";
+    } else if (i < kReceivedEntropyOffset) {
+      expected_error = "Unable to read least unacked delta.";
+    } else if (i < kLargestObservedOffset) {
+      expected_error = "Unable to read entropy hash for received packets.";
+    } else if (i < kMissingDeltaTimeOffset) {
+      expected_error = "Unable to read largest observed.";
+    } else if (i < kNumMissingPacketOffset) {
+      expected_error = "Unable to read delta time largest observed.";
+    } else if (i < kMissingPacketsOffset) {
+      expected_error = "Unable to read num missing packet ranges.";
+    } else if (i < kMissingPacketsRange) {
+      expected_error = "Unable to read missing sequence number delta.";
+    } else if (i < kRevivedPacketsLength) {
+      expected_error = "Unable to read missing sequence number range.";
+    } else {
+      expected_error = "Unable to read num revived packets.";
+    }
+    CheckProcessingFails(
+        packet,
+        i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+                                PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+        expected_error, QUIC_INVALID_ACK_DATA);
+  }
+}
+
+TEST_P(QuicFramerTest, AckFrameRevivedPackets) {
+  if (framer_.version() <= QUIC_VERSION_14) {
+    return;
+  }
+
+  unsigned char packet[] = {
+    // public flags (8 byte guid)
+    0x3C,
+    // guid
+    0x10, 0x32, 0x54, 0x76,
+    0x98, 0xBA, 0xDC, 0xFE,
+    // packet sequence number
+    0xA8, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // private flags (entropy)
+    0x01,
+
+    // frame type (ack frame)
+    // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+    0x6C,
+    // entropy hash of sent packets till least awaiting - 1.
+    0xAB,
+    // least packet sequence number awaiting an ack, delta from sequence number.
+    0x08, 0x00, 0x00, 0x00,
+    0x00, 0x00,
+    // entropy hash of all received packets.
+    0xBA,
+    // largest observed packet sequence number
+    0xBF, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // Zero delta time.
+    0x0, 0x0,
+    // num missing packets
+    0x01,
+    // missing packet delta
+    0x01,
+    // 0 more missing packets in range.
+    0x00,
+    // Number of revived packets.
+    0x01,
+    // Revived packet sequence number.
+    0xBE, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+  };
+
+  QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+  EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+  EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+  EXPECT_EQ(0u, visitor_.stream_frames_.size());
+  ASSERT_EQ(1u, visitor_.ack_frames_.size());
+  const QuicAckFrame& frame = *visitor_.ack_frames_[0];
+  EXPECT_EQ(0xAB, frame.sent_info.entropy_hash);
+  EXPECT_EQ(0xBA, frame.received_info.entropy_hash);
+  EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.received_info.largest_observed);
+  ASSERT_EQ(1u, frame.received_info.missing_packets.size());
+  SequenceNumberSet::const_iterator missing_iter =
+      frame.received_info.missing_packets.begin();
+  EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter);
+  EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame.sent_info.least_unacked);
+
+  const size_t kSentEntropyOffset = kQuicFrameTypeSize;
+  const size_t kLeastUnackedOffset = kSentEntropyOffset + kQuicEntropyHashSize;
+  const size_t kReceivedEntropyOffset = kLeastUnackedOffset +
+      PACKET_6BYTE_SEQUENCE_NUMBER;
+  const size_t kLargestObservedOffset = kReceivedEntropyOffset +
+      kQuicEntropyHashSize;
+  const size_t kMissingDeltaTimeOffset = kLargestObservedOffset +
+      PACKET_6BYTE_SEQUENCE_NUMBER;
+  const size_t kNumMissingPacketOffset = kMissingDeltaTimeOffset +
+      kQuicDeltaTimeLargestObservedSize;
+  const size_t kMissingPacketsOffset = kNumMissingPacketOffset +
+      kNumberOfMissingPacketsSize;
+  const size_t kMissingPacketsRange = kMissingPacketsOffset +
+      PACKET_1BYTE_SEQUENCE_NUMBER;
+  const size_t kRevivedPacketsLength = kMissingPacketsRange +
+      PACKET_1BYTE_SEQUENCE_NUMBER;
+  const size_t kRevivedPacketSequenceNumberLength = kRevivedPacketsLength +
+      PACKET_1BYTE_SEQUENCE_NUMBER;
+  // Now test framing boundaries
+  const size_t ack_frame_size = kRevivedPacketSequenceNumberLength +
+      PACKET_6BYTE_SEQUENCE_NUMBER;
+  for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) {
+    string expected_error;
+    if (i < kLeastUnackedOffset) {
+      expected_error = "Unable to read entropy hash for sent packets.";
+    } else if (i < kReceivedEntropyOffset) {
+      expected_error = "Unable to read least unacked delta.";
+    } else if (i < kLargestObservedOffset) {
+      expected_error = "Unable to read entropy hash for received packets.";
+    } else if (i < kMissingDeltaTimeOffset) {
+      expected_error = "Unable to read largest observed.";
+    } else if (i < kNumMissingPacketOffset) {
+      expected_error = "Unable to read delta time largest observed.";
+    } else if (i < kMissingPacketsOffset) {
+      expected_error = "Unable to read num missing packet ranges.";
+    } else if (i < kMissingPacketsRange) {
+      expected_error = "Unable to read missing sequence number delta.";
+    } else if (i < kRevivedPacketsLength) {
+      expected_error = "Unable to read missing sequence number range.";
+    } else if (i < kRevivedPacketSequenceNumberLength) {
+      expected_error = "Unable to read num revived packets.";
+    } else {
+      expected_error = "Unable to read revived packet.";
+    }
+    CheckProcessingFails(
+        packet,
+        i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+                                PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+        expected_error, QUIC_INVALID_ACK_DATA);
+  }
+}
+
 TEST_P(QuicFramerTest, AckFrameNoNacks) {
   unsigned char packet[] = {
     // public flags (8 byte guid)
@@ -1795,6 +2045,9 @@ TEST_P(QuicFramerTest, AckFrameNoNacks) {
 }
 
 TEST_P(QuicFramerTest, AckFrame500Nacks) {
+  if (framer_.version() <= QUIC_VERSION_14) {
+    return;
+  }
   unsigned char packet[] = {
     // public flags (8 byte guid)
     0x3C,
@@ -1833,6 +2086,8 @@ TEST_P(QuicFramerTest, AckFrame500Nacks) {
     0x00,
     // 255 more missing packets in range.
     0xFF,
+    // No revived packets.
+    0x00,
   };
 
   QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
@@ -1849,6 +2104,7 @@ TEST_P(QuicFramerTest, AckFrame500Nacks) {
   EXPECT_EQ(0xBA, frame->received_info.entropy_hash);
   EXPECT_EQ(GG_UINT64_C(0x0123456789ABF),
             frame->received_info.largest_observed);
+  EXPECT_EQ(0u, frame->received_info.revived_packets.size());
   ASSERT_EQ(500u, frame->received_info.missing_packets.size());
   SequenceNumberSet::const_iterator first_missing_iter =
       frame->received_info.missing_packets.begin();
@@ -1870,7 +2126,10 @@ TEST_P(QuicFramerTest, AckFrame500Nacks) {
                                       AsChars(packet), arraysize(packet));
 }
 
-TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) {
+TEST_P(QuicFramerTest, AckFrame500NacksV14) {
+  if (framer_.version() > QUIC_VERSION_14) {
+    return;
+  }
   unsigned char packet[] = {
     // public flags (8 byte guid)
     0x3C,
@@ -1878,17 +2137,94 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) {
     0x10, 0x32, 0x54, 0x76,
     0x98, 0xBA, 0xDC, 0xFE,
     // packet sequence number
-    0xBC, 0x9A, 0x78, 0x56,
+    0xA8, 0x9A, 0x78, 0x56,
     0x34, 0x12,
-    // private flags
-    0x00,
+    // private flags (entropy)
+    0x01,
 
-    // frame type (congestion feedback frame)
-    0x20,
-    // congestion feedback type (tcp)
+    // frame type (ack frame)
+    // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+    0x6C,
+    // entropy hash of sent packets till least awaiting - 1.
+    0xAB,
+    // least packet sequence number awaiting an ack, delta from sequence number.
+    0x08, 0x00, 0x00, 0x00,
+    0x00, 0x00,
+    // entropy hash of all received packets.
+    0xBA,
+    // largest observed packet sequence number
+    0xBF, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // Zero delta time.
+    0x0, 0x0,
+    // num missing packet ranges
+    0x02,
+    // missing packet delta
+    0x01,
+    // 243 more missing packets in range.
+    // The ranges are listed in this order so the re-constructed packet matches.
+    0xF3,
+    // No gap between ranges
+    0x00,
+    // 255 more missing packets in range.
+    0xFF,
+  };
+
+  QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+  EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+  EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+  EXPECT_EQ(0u, visitor_.stream_frames_.size());
+  ASSERT_EQ(1u, visitor_.ack_frames_.size());
+  QuicAckFrame* frame = visitor_.ack_frames_[0];
+  EXPECT_EQ(0xAB, frame->sent_info.entropy_hash);
+  EXPECT_EQ(0xBA, frame->received_info.entropy_hash);
+  EXPECT_EQ(GG_UINT64_C(0x0123456789ABF),
+            frame->received_info.largest_observed);
+  ASSERT_EQ(500u, frame->received_info.missing_packets.size());
+  SequenceNumberSet::const_iterator first_missing_iter =
+      frame->received_info.missing_packets.begin();
+  EXPECT_EQ(GG_UINT64_C(0x0123456789ABE) - 499, *first_missing_iter);
+  SequenceNumberSet::const_reverse_iterator last_missing_iter =
+      frame->received_info.missing_packets.rbegin();
+  EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *last_missing_iter);
+  EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame->sent_info.least_unacked);
+
+  // Verify that the packet re-serializes identically.
+  QuicFrames frames;
+  frames.push_back(QuicFrame(frame));
+  scoped_ptr<QuicPacket> data(
+      framer_.BuildUnsizedDataPacket(*visitor_.header_, frames).packet);
+  ASSERT_TRUE(data != NULL);
+
+  test::CompareCharArraysWithHexError("constructed packet",
+                                      data->data(), data->length(),
+                                      AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) {
+  if (framer_.version() <= QUIC_VERSION_14) {
+    return;
+  }
+  unsigned char packet[] = {
+    // public flags (8 byte guid)
+    0x3C,
+    // guid
+    0x10, 0x32, 0x54, 0x76,
+    0x98, 0xBA, 0xDC, 0xFE,
+    // packet sequence number
+    0xBC, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // private flags
+    0x00,
+
+    // frame type (congestion feedback frame)
+    0x20,
+    // congestion feedback type (tcp)
     0x00,
-    // ack_frame.feedback.tcp.accumulated_number_of_lost_packets
-    0x01, 0x02,
     // ack_frame.feedback.tcp.receive_window
     0x03, 0x04,
   };
@@ -1905,8 +2241,62 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) {
   const QuicCongestionFeedbackFrame& frame =
       *visitor_.congestion_feedback_frames_[0];
   ASSERT_EQ(kTCP, frame.type);
-  EXPECT_EQ(0x0201,
-            frame.tcp.accumulated_number_of_lost_packets);
+  EXPECT_EQ(0x4030u, frame.tcp.receive_window);
+
+  // Now test framing boundaries
+  for (size_t i = kQuicFrameTypeSize; i < 4; ++i) {
+    string expected_error;
+    if (i < 2) {
+      expected_error = "Unable to read congestion feedback type.";
+    } else if (i < 4) {
+      expected_error = "Unable to read receive window.";
+    }
+    CheckProcessingFails(
+        packet,
+        i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+                                PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+        expected_error, QUIC_INVALID_CONGESTION_FEEDBACK_DATA);
+  }
+}
+
+TEST_P(QuicFramerTest, CongestionFeedbackFrameTCPV14) {
+  if (framer_.version() > QUIC_VERSION_14) {
+    return;
+  }
+  unsigned char packet[] = {
+    // public flags (8 byte guid)
+    0x3C,
+    // guid
+    0x10, 0x32, 0x54, 0x76,
+    0x98, 0xBA, 0xDC, 0xFE,
+    // packet sequence number
+    0xBC, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // private flags
+    0x00,
+
+    // frame type (congestion feedback frame)
+    0x20,
+    // congestion feedback type (tcp)
+    0x00,
+    // ack_frame.feedback.tcp.accumulated_number_of_lost_packets
+    0x01, 0x02,
+    // ack_frame.feedback.tcp.receive_window
+    0x03, 0x04,
+  };
+
+  QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+  EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+  EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+  EXPECT_EQ(0u, visitor_.stream_frames_.size());
+  ASSERT_EQ(1u, visitor_.congestion_feedback_frames_.size());
+  const QuicCongestionFeedbackFrame& frame =
+      *visitor_.congestion_feedback_frames_[0];
+  ASSERT_EQ(kTCP, frame.type);
   EXPECT_EQ(0x4030u, frame.tcp.receive_window);
 
   // Now test framing boundaries
@@ -1928,6 +2318,102 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) {
 }
 
 TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) {
+  if (framer_.version() <= QUIC_VERSION_14) {
+    return;
+  }
+  unsigned char packet[] = {
+    // public flags (8 byte guid)
+    0x3C,
+    // guid
+    0x10, 0x32, 0x54, 0x76,
+    0x98, 0xBA, 0xDC, 0xFE,
+    // packet sequence number
+    0xBC, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // private flags
+    0x00,
+
+    // frame type (congestion feedback frame)
+    0x20,
+    // congestion feedback type (inter arrival)
+    0x01,
+    // num received packets
+    0x03,
+    // lowest sequence number
+    0xBA, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // receive time
+    0x87, 0x96, 0xA5, 0xB4,
+    0xC3, 0xD2, 0xE1, 0x07,
+    // sequence delta
+    0x01, 0x00,
+    // time delta
+    0x01, 0x00, 0x00, 0x00,
+    // sequence delta (skip one packet)
+    0x03, 0x00,
+    // time delta
+    0x02, 0x00, 0x00, 0x00,
+  };
+
+  QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+  EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+  EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+  EXPECT_EQ(0u, visitor_.stream_frames_.size());
+  ASSERT_EQ(1u, visitor_.congestion_feedback_frames_.size());
+  const QuicCongestionFeedbackFrame& frame =
+      *visitor_.congestion_feedback_frames_[0];
+  ASSERT_EQ(kInterArrival, frame.type);
+  ASSERT_EQ(3u, frame.inter_arrival.received_packet_times.size());
+  TimeMap::const_iterator iter =
+      frame.inter_arrival.received_packet_times.begin();
+  EXPECT_EQ(GG_UINT64_C(0x0123456789ABA), iter->first);
+  EXPECT_EQ(GG_INT64_C(0x07E1D2C3B4A59687),
+            iter->second.Subtract(start_).ToMicroseconds());
+  ++iter;
+  EXPECT_EQ(GG_UINT64_C(0x0123456789ABB), iter->first);
+  EXPECT_EQ(GG_INT64_C(0x07E1D2C3B4A59688),
+            iter->second.Subtract(start_).ToMicroseconds());
+  ++iter;
+  EXPECT_EQ(GG_UINT64_C(0x0123456789ABD), iter->first);
+  EXPECT_EQ(GG_INT64_C(0x07E1D2C3B4A59689),
+            iter->second.Subtract(start_).ToMicroseconds());
+
+  // Now test framing boundaries
+  for (size_t i = kQuicFrameTypeSize; i < 29; ++i) {
+    string expected_error;
+    if (i < 2) {
+      expected_error = "Unable to read congestion feedback type.";
+    } else if (i < 3) {
+      expected_error = "Unable to read num received packets.";
+    } else if (i < 9) {
+      expected_error = "Unable to read smallest received.";
+    } else if (i < 17) {
+      expected_error = "Unable to read time received.";
+    } else if (i < 19) {
+      expected_error = "Unable to read sequence delta in received packets.";
+    } else if (i < 23) {
+      expected_error = "Unable to read time delta in received packets.";
+    } else if (i < 25) {
+      expected_error = "Unable to read sequence delta in received packets.";
+    } else if (i < 29) {
+      expected_error = "Unable to read time delta in received packets.";
+    }
+    CheckProcessingFails(
+        packet,
+        i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+                                PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+        expected_error, QUIC_INVALID_CONGESTION_FEEDBACK_DATA);
+  }
+}
+
+TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrivalV14) {
+  if (framer_.version() > QUIC_VERSION_14) {
+    return;
+  }
   unsigned char packet[] = {
     // public flags (8 byte guid)
     0x3C,
@@ -1976,8 +2462,6 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) {
   const QuicCongestionFeedbackFrame& frame =
       *visitor_.congestion_feedback_frames_[0];
   ASSERT_EQ(kInterArrival, frame.type);
-  EXPECT_EQ(0x0302, frame.inter_arrival.
-            accumulated_number_of_lost_packets);
   ASSERT_EQ(3u, frame.inter_arrival.received_packet_times.size());
   TimeMap::const_iterator iter =
       frame.inter_arrival.received_packet_times.begin();
@@ -2100,7 +2584,11 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameInvalidFeedback) {
   EXPECT_EQ(QUIC_INVALID_CONGESTION_FEEDBACK_DATA, framer_.error());
 }
 
-TEST_P(QuicFramerTest, RstStreamFrame) {
+TEST_P(QuicFramerTest, RstStreamFrameVersion13) {
+  if (version_ > QUIC_VERSION_13) {
+    return;
+  }
+
   unsigned char packet[] = {
     // public flags (8 byte guid)
     0x3C,
@@ -2141,7 +2629,8 @@ TEST_P(QuicFramerTest, RstStreamFrame) {
   EXPECT_EQ("because I can", visitor_.rst_stream_frame_.error_details);
 
   // Now test framing boundaries
-  for (size_t i = kQuicFrameTypeSize; i < 24; ++i) {
+  for (size_t i = kQuicFrameTypeSize;
+       i < QuicFramer::GetMinRstStreamFrameSize(version_); ++i) {
     string expected_error;
     if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) {
       expected_error = "Unable to read stream_id.";
@@ -2159,6 +2648,80 @@ TEST_P(QuicFramerTest, RstStreamFrame) {
   }
 }
 
+TEST_P(QuicFramerTest, RstStreamFrameQuic) {
+  if (version_ <= QUIC_VERSION_13) {
+    return;
+  }
+
+  unsigned char packet[] = {
+    // public flags (8 byte guid)
+    0x3C,
+    // guid
+    0x10, 0x32, 0x54, 0x76,
+    0x98, 0xBA, 0xDC, 0xFE,
+    // packet sequence number
+    0xBC, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // private flags
+    0x00,
+
+    // frame type (rst stream frame)
+    0x01,
+    // stream id
+    0x04, 0x03, 0x02, 0x01,
+
+    // sent byte offset
+    0x01, 0x02, 0x03, 0x04,
+    0x05, 0x06, 0x07, 0x08,
+
+    // error code
+    0x01, 0x00, 0x00, 0x00,
+
+    // error details length
+    0x0d, 0x00,
+    // error details
+    'b',  'e',  'c',  'a',
+    'u',  's',  'e',  ' ',
+    'I',  ' ',  'c',  'a',
+    'n',
+  };
+
+  QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+  EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+  EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+  EXPECT_EQ(GG_UINT64_C(0x01020304), visitor_.rst_stream_frame_.stream_id);
+  EXPECT_EQ(0x01, visitor_.rst_stream_frame_.error_code);
+  EXPECT_EQ("because I can", visitor_.rst_stream_frame_.error_details);
+  EXPECT_EQ(GG_UINT64_C(0x0807060504030201),
+            visitor_.rst_stream_frame_.byte_offset);
+
+  // Now test framing boundaries
+  for (size_t i = kQuicFrameTypeSize;
+       i < QuicFramer::GetMinRstStreamFrameSize(version_); ++i) {
+    string expected_error;
+    if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) {
+      expected_error = "Unable to read stream_id.";
+    } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize +
+                       + kQuicMaxStreamOffsetSize) {
+      expected_error = "Unable to read rst stream sent byte offset.";
+    } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize +
+                       + kQuicMaxStreamOffsetSize + kQuicErrorCodeSize) {
+      expected_error = "Unable to read rst stream error code.";
+    } else {
+      expected_error = "Unable to read rst stream error details.";
+    }
+    CheckProcessingFails(
+        packet,
+        i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+                                PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+        expected_error, QUIC_INVALID_RST_STREAM_DATA);
+  }
+}
+
 TEST_P(QuicFramerTest, ConnectionCloseFrame) {
   unsigned char packet[] = {
     // public flags (8 byte guid)
@@ -2217,68 +2780,347 @@ TEST_P(QuicFramerTest, ConnectionCloseFrame) {
   }
 }
 
-TEST_P(QuicFramerTest, GoAwayFrame) {
+TEST_P(QuicFramerTest, GoAwayFrame) {
+  unsigned char packet[] = {
+    // public flags (8 byte guid)
+    0x3C,
+    // guid
+    0x10, 0x32, 0x54, 0x76,
+    0x98, 0xBA, 0xDC, 0xFE,
+    // packet sequence number
+    0xBC, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // private flags
+    0x00,
+
+    // frame type (go away frame)
+    0x03,
+    // error code
+    0x09, 0x00, 0x00, 0x00,
+    // stream id
+    0x04, 0x03, 0x02, 0x01,
+    // error details length
+    0x0d, 0x00,
+    // error details
+    'b',  'e',  'c',  'a',
+    'u',  's',  'e',  ' ',
+    'I',  ' ',  'c',  'a',
+    'n',
+  };
+
+  QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+  EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+  EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+  EXPECT_EQ(GG_UINT64_C(0x01020304),
+            visitor_.goaway_frame_.last_good_stream_id);
+  EXPECT_EQ(0x9, visitor_.goaway_frame_.error_code);
+  EXPECT_EQ("because I can", visitor_.goaway_frame_.reason_phrase);
+
+  const size_t reason_size = arraysize("because I can") - 1;
+  // Now test framing boundaries
+  for (size_t i = kQuicFrameTypeSize;
+       i < QuicFramer::GetMinGoAwayFrameSize() + reason_size; ++i) {
+    string expected_error;
+    if (i < kQuicFrameTypeSize + kQuicErrorCodeSize) {
+      expected_error = "Unable to read go away error code.";
+    } else if (i < kQuicFrameTypeSize + kQuicErrorCodeSize +
+               kQuicMaxStreamIdSize) {
+      expected_error = "Unable to read last good stream id.";
+    } else {
+      expected_error = "Unable to read goaway reason.";
+    }
+    CheckProcessingFails(
+        packet,
+        i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+                                PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+        expected_error, QUIC_INVALID_GOAWAY_DATA);
+  }
+}
+
+TEST_P(QuicFramerTest, WindowUpdateFrame) {
+  unsigned char packet[] = {
+    // public flags (8 byte guid)
+    0x3C,
+    // guid
+    0x10, 0x32, 0x54, 0x76,
+    0x98, 0xBA, 0xDC, 0xFE,
+    // packet sequence number
+    0xBC, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // private flags
+    0x00,
+
+    // frame type (window update frame)
+    0x04,
+    // stream id
+    0x04, 0x03, 0x02, 0x01,
+    // byte offset
+    0x05, 0x06, 0x07, 0x08,
+    0x09, 0x0a, 0x0b, 0x0c,
+  };
+
+  QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+
+  // WINDOW_UPDATE frame introduced in QUIC_VERSION_14.
+  if (version_ <= QUIC_VERSION_13) {
+    string expected_error = "Trying to read a WindowUpdateFrame in " +
+                            QuicVersionToString(version_);
+    EXPECT_DFATAL(framer_.ProcessPacket(encrypted), expected_error);
+    return;
+  }
+
+  EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+  EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+  EXPECT_EQ(GG_UINT64_C(0x01020304),
+            visitor_.window_update_frame_.stream_id);
+  EXPECT_EQ(GG_UINT64_C(0x0c0b0a0908070605),
+            visitor_.window_update_frame_.byte_offset);
+
+  // Now test framing boundaries
+  for (size_t i = kQuicFrameTypeSize;
+       i < QuicFramer::GetWindowUpdateFrameSize(); ++i) {
+    string expected_error;
+    if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) {
+      expected_error = "Unable to read stream_id.";
+    } else {
+      expected_error = "Unable to read window byte_offset.";
+    }
+    CheckProcessingFails(
+        packet,
+        i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+                                PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+        expected_error, QUIC_INVALID_WINDOW_UPDATE_DATA);
+  }
+}
+
+TEST_P(QuicFramerTest, BlockedFrame) {
+  unsigned char packet[] = {
+    // public flags (8 byte guid)
+    0x3C,
+    // guid
+    0x10, 0x32, 0x54, 0x76,
+    0x98, 0xBA, 0xDC, 0xFE,
+    // packet sequence number
+    0xBC, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // private flags
+    0x00,
+
+    // frame type (blocked frame)
+    0x05,
+    // stream id
+    0x04, 0x03, 0x02, 0x01,
+  };
+
+  QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+
+  // BLOCKED frame introduced in QUIC_VERSION_14.
+  if (version_ <= QUIC_VERSION_13) {
+    string expected_error =
+        "Trying to read a BlockedFrame in " + QuicVersionToString(version_);
+    EXPECT_DFATAL(framer_.ProcessPacket(encrypted), expected_error);
+    return;
+  }
+
+  EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+  EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+  EXPECT_EQ(GG_UINT64_C(0x01020304),
+            visitor_.blocked_frame_.stream_id);
+
+  // Now test framing boundaries
+  for (size_t i = kQuicFrameTypeSize; i < QuicFramer::GetBlockedFrameSize();
+       ++i) {
+    string expected_error = "Unable to read stream_id.";
+    CheckProcessingFails(
+        packet,
+        i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+                                PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+        expected_error, QUIC_INVALID_BLOCKED_DATA);
+  }
+}
+
+TEST_P(QuicFramerTest, PublicResetPacket) {
+  unsigned char packet[] = {
+    // public flags (public reset, 8 byte guid)
+    0x0E,
+    // guid
+    0x10, 0x32, 0x54, 0x76,
+    0x98, 0xBA, 0xDC, 0xFE,
+    // message tag (kPRST)
+    'P', 'R', 'S', 'T',
+    // num_entries (2) + padding
+    0x02, 0x00, 0x00, 0x00,
+    // tag kRNON
+    'R', 'N', 'O', 'N',
+    // end offset 8
+    0x08, 0x00, 0x00, 0x00,
+    // tag kRSEQ
+    'R', 'S', 'E', 'Q',
+    // end offset 16
+    0x10, 0x00, 0x00, 0x00,
+    // nonce proof
+    0x89, 0x67, 0x45, 0x23,
+    0x01, 0xEF, 0xCD, 0xAB,
+    // rejected sequence number
+    0xBC, 0x9A, 0x78, 0x56,
+    0x34, 0x12, 0x00, 0x00,
+  };
+
+  QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+  EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+  ASSERT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.public_reset_packet_.get());
+  EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
+            visitor_.public_reset_packet_->public_header.guid);
+  EXPECT_TRUE(visitor_.public_reset_packet_->public_header.reset_flag);
+  EXPECT_FALSE(visitor_.public_reset_packet_->public_header.version_flag);
+  EXPECT_EQ(GG_UINT64_C(0xABCDEF0123456789),
+            visitor_.public_reset_packet_->nonce_proof);
+  EXPECT_EQ(GG_UINT64_C(0x123456789ABC),
+            visitor_.public_reset_packet_->rejected_sequence_number);
+  EXPECT_TRUE(
+      visitor_.public_reset_packet_->client_address.address().empty());
+
+  // Now test framing boundaries
+  for (size_t i = 0; i < arraysize(packet); ++i) {
+    string expected_error;
+    DVLOG(1) << "iteration: " << i;
+    if (i < kGuidOffset) {
+      expected_error = "Unable to read public flags.";
+      CheckProcessingFails(packet, i, expected_error,
+                           QUIC_INVALID_PACKET_HEADER);
+    } else if (i < kPublicResetPacketMessageTagOffset) {
+      expected_error = "Unable to read GUID.";
+      CheckProcessingFails(packet, i, expected_error,
+                           QUIC_INVALID_PACKET_HEADER);
+    } else {
+      expected_error = "Unable to read reset message.";
+      CheckProcessingFails(packet, i, expected_error,
+                           QUIC_INVALID_PUBLIC_RST_PACKET);
+    }
+  }
+}
+
+TEST_P(QuicFramerTest, PublicResetPacketWithTrailingJunk) {
+  unsigned char packet[] = {
+    // public flags (public reset, 8 byte guid)
+    0x0E,
+    // guid
+    0x10, 0x32, 0x54, 0x76,
+    0x98, 0xBA, 0xDC, 0xFE,
+    // message tag (kPRST)
+    'P', 'R', 'S', 'T',
+    // num_entries (2) + padding
+    0x02, 0x00, 0x00, 0x00,
+    // tag kRNON
+    'R', 'N', 'O', 'N',
+    // end offset 8
+    0x08, 0x00, 0x00, 0x00,
+    // tag kRSEQ
+    'R', 'S', 'E', 'Q',
+    // end offset 16
+    0x10, 0x00, 0x00, 0x00,
+    // nonce proof
+    0x89, 0x67, 0x45, 0x23,
+    0x01, 0xEF, 0xCD, 0xAB,
+    // rejected sequence number
+    0xBC, 0x9A, 0x78, 0x56,
+    0x34, 0x12, 0x00, 0x00,
+    // trailing junk
+    'j', 'u', 'n', 'k',
+  };
+
+  string expected_error = "Unable to read reset message.";
+  CheckProcessingFails(packet, arraysize(packet), expected_error,
+                       QUIC_INVALID_PUBLIC_RST_PACKET);
+}
+
+TEST_P(QuicFramerTest, PublicResetPacketWithClientAddress) {
   unsigned char packet[] = {
-    // public flags (8 byte guid)
-    0x3C,
+    // public flags (public reset, 8 byte guid)
+    0x0E,
     // guid
     0x10, 0x32, 0x54, 0x76,
     0x98, 0xBA, 0xDC, 0xFE,
-    // packet sequence number
+    // message tag (kPRST)
+    'P', 'R', 'S', 'T',
+    // num_entries (3) + padding
+    0x03, 0x00, 0x00, 0x00,
+    // tag kRNON
+    'R', 'N', 'O', 'N',
+    // end offset 8
+    0x08, 0x00, 0x00, 0x00,
+    // tag kRSEQ
+    'R', 'S', 'E', 'Q',
+    // end offset 16
+    0x10, 0x00, 0x00, 0x00,
+    // tag kCADR
+    'C', 'A', 'D', 'R',
+    // end offset 24
+    0x18, 0x00, 0x00, 0x00,
+    // nonce proof
+    0x89, 0x67, 0x45, 0x23,
+    0x01, 0xEF, 0xCD, 0xAB,
+    // rejected sequence number
     0xBC, 0x9A, 0x78, 0x56,
-    0x34, 0x12,
-    // private flags
-    0x00,
-
-    // frame type (go away frame)
-    0x03,
-    // error code
-    0x09, 0x00, 0x00, 0x00,
-    // stream id
-    0x04, 0x03, 0x02, 0x01,
-    // error details length
-    0x0d, 0x00,
-    // error details
-    'b',  'e',  'c',  'a',
-    'u',  's',  'e',  ' ',
-    'I',  ' ',  'c',  'a',
-    'n',
+    0x34, 0x12, 0x00, 0x00,
+    // client address: 4.31.198.44:443
+    0x02, 0x00,
+    0x04, 0x1F, 0xC6, 0x2C,
+    0xBB, 0x01,
   };
 
   QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
   EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+  ASSERT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.public_reset_packet_.get());
+  EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
+            visitor_.public_reset_packet_->public_header.guid);
+  EXPECT_TRUE(visitor_.public_reset_packet_->public_header.reset_flag);
+  EXPECT_FALSE(visitor_.public_reset_packet_->public_header.version_flag);
+  EXPECT_EQ(GG_UINT64_C(0xABCDEF0123456789),
+            visitor_.public_reset_packet_->nonce_proof);
+  EXPECT_EQ(GG_UINT64_C(0x123456789ABC),
+            visitor_.public_reset_packet_->rejected_sequence_number);
+  EXPECT_EQ("4.31.198.44",
+            IPAddressToString(visitor_.public_reset_packet_->
+                client_address.address()));
+  EXPECT_EQ(443, visitor_.public_reset_packet_->client_address.port());
 
-  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
-  ASSERT_TRUE(visitor_.header_.get());
-  EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
-
-  EXPECT_EQ(GG_UINT64_C(0x01020304),
-            visitor_.goaway_frame_.last_good_stream_id);
-  EXPECT_EQ(0x9, visitor_.goaway_frame_.error_code);
-  EXPECT_EQ("because I can", visitor_.goaway_frame_.reason_phrase);
-
-  const size_t reason_size = arraysize("because I can") - 1;
   // Now test framing boundaries
-  for (size_t i = kQuicFrameTypeSize;
-       i < QuicFramer::GetMinGoAwayFrameSize() + reason_size; ++i) {
+  for (size_t i = 0; i < arraysize(packet); ++i) {
     string expected_error;
-    if (i < kQuicFrameTypeSize + kQuicErrorCodeSize) {
-      expected_error = "Unable to read go away error code.";
-    } else if (i < kQuicFrameTypeSize + kQuicErrorCodeSize +
-               kQuicMaxStreamIdSize) {
-      expected_error = "Unable to read last good stream id.";
+    DVLOG(1) << "iteration: " << i;
+    if (i < kGuidOffset) {
+      expected_error = "Unable to read public flags.";
+      CheckProcessingFails(packet, i, expected_error,
+                           QUIC_INVALID_PACKET_HEADER);
+    } else if (i < kPublicResetPacketMessageTagOffset) {
+      expected_error = "Unable to read GUID.";
+      CheckProcessingFails(packet, i, expected_error,
+                           QUIC_INVALID_PACKET_HEADER);
     } else {
-      expected_error = "Unable to read goaway reason.";
+      expected_error = "Unable to read reset message.";
+      CheckProcessingFails(packet, i, expected_error,
+                           QUIC_INVALID_PUBLIC_RST_PACKET);
     }
-    CheckProcessingFails(
-        packet,
-        i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
-                                PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
-        expected_error, QUIC_INVALID_GOAWAY_DATA);
   }
 }
 
-TEST_P(QuicFramerTest, PublicResetPacket) {
+// TODO(wtc): remove this test when we drop support for QUIC_VERSION_13.
+TEST_P(QuicFramerTest, PublicResetPacketOld) {
   unsigned char packet[] = {
     // public flags (public reset, 8 byte guid)
     0x3E,
@@ -2305,9 +3147,11 @@ TEST_P(QuicFramerTest, PublicResetPacket) {
             visitor_.public_reset_packet_->nonce_proof);
   EXPECT_EQ(GG_UINT64_C(0x123456789ABC),
             visitor_.public_reset_packet_->rejected_sequence_number);
+  EXPECT_TRUE(
+      visitor_.public_reset_packet_->client_address.address().empty());
 
   // Now test framing boundaries
-  for (size_t i = 0; i < GetPublicResetPacketSize(); ++i) {
+  for (size_t i = 0; i < arraysize(packet); ++i) {
     string expected_error;
     DVLOG(1) << "iteration: " << i;
     if (i < kGuidOffset) {
@@ -2708,7 +3552,7 @@ TEST_P(QuicFramerTest, BuildVersionNegotiationPacket) {
 
   unsigned char packet[] = {
     // public flags (version, 8 byte guid)
-    0x3D,
+    0x0D,
     // guid
     0x10, 0x32, 0x54, 0x76,
     0x98, 0xBA, 0xDC, 0xFE,
@@ -2726,27 +3570,214 @@ TEST_P(QuicFramerTest, BuildVersionNegotiationPacket) {
                                       AsChars(packet), arraysize(packet));
 }
 
-TEST_P(QuicFramerTest, BuildAckFramePacket) {
+TEST_P(QuicFramerTest, BuildAckFramePacket) {
+  if (version_ <= QUIC_VERSION_14) {
+    return;
+  }
+  QuicPacketHeader header;
+  header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+  header.public_header.reset_flag = false;
+  header.public_header.version_flag = false;
+  header.fec_flag = false;
+  header.entropy_flag = true;
+  header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8);
+  header.fec_group = 0;
+
+  QuicAckFrame ack_frame;
+  ack_frame.received_info.entropy_hash = 0x43;
+  ack_frame.received_info.largest_observed = GG_UINT64_C(0x770123456789ABF);
+  ack_frame.received_info.delta_time_largest_observed = QuicTime::Delta::Zero();
+  ack_frame.received_info.missing_packets.insert(
+      GG_UINT64_C(0x770123456789ABE));
+  ack_frame.sent_info.entropy_hash = 0x14;
+  ack_frame.sent_info.least_unacked = GG_UINT64_C(0x770123456789AA0);
+
+  QuicFrames frames;
+  frames.push_back(QuicFrame(&ack_frame));
+
+  unsigned char packet[] = {
+    // public flags (8 byte guid)
+    0x3C,
+    // guid
+    0x10, 0x32, 0x54, 0x76,
+    0x98, 0xBA, 0xDC, 0xFE,
+    // packet sequence number
+    0xA8, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // private flags (entropy)
+    0x01,
+
+    // frame type (ack frame)
+    // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+    0x6C,
+    // entropy hash of sent packets till least awaiting - 1.
+    0x14,
+    // least packet sequence number awaiting an ack, delta from sequence number.
+    0x08, 0x00, 0x00, 0x00,
+    0x00, 0x00,
+    // entropy hash of all received packets.
+    0x43,
+    // largest observed packet sequence number
+    0xBF, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // Zero delta time.
+    0x0, 0x0,
+    // num missing packet ranges
+    0x01,
+    // missing packet delta
+    0x01,
+    // 0 more missing packets in range.
+    0x00,
+    // 0 revived packets.
+    0x00,
+  };
+
+  scoped_ptr<QuicPacket> data(
+      framer_.BuildUnsizedDataPacket(header, frames).packet);
+  ASSERT_TRUE(data != NULL);
+
+  test::CompareCharArraysWithHexError("constructed packet",
+                                      data->data(), data->length(),
+                                      AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildAckFramePacketV14) {
+  if (version_ > QUIC_VERSION_14) {
+    return;
+  }
+  QuicPacketHeader header;
+  header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+  header.public_header.reset_flag = false;
+  header.public_header.version_flag = false;
+  header.fec_flag = false;
+  header.entropy_flag = true;
+  header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8);
+  header.fec_group = 0;
+
+  QuicAckFrame ack_frame;
+  ack_frame.received_info.entropy_hash = 0x43;
+  ack_frame.received_info.largest_observed = GG_UINT64_C(0x770123456789ABF);
+  ack_frame.received_info.delta_time_largest_observed = QuicTime::Delta::Zero();
+  ack_frame.received_info.missing_packets.insert(
+      GG_UINT64_C(0x770123456789ABE));
+  ack_frame.sent_info.entropy_hash = 0x14;
+  ack_frame.sent_info.least_unacked = GG_UINT64_C(0x770123456789AA0);
+
+  QuicFrames frames;
+  frames.push_back(QuicFrame(&ack_frame));
+
+  unsigned char packet[] = {
+    // public flags (8 byte guid)
+    0x3C,
+    // guid
+    0x10, 0x32, 0x54, 0x76,
+    0x98, 0xBA, 0xDC, 0xFE,
+    // packet sequence number
+    0xA8, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // private flags (entropy)
+    0x01,
+
+    // frame type (ack frame)
+    // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+    0x6C,
+    // entropy hash of sent packets till least awaiting - 1.
+    0x14,
+    // least packet sequence number awaiting an ack, delta from sequence number.
+    0x08, 0x00, 0x00, 0x00,
+    0x00, 0x00,
+    // entropy hash of all received packets.
+    0x43,
+    // largest observed packet sequence number
+    0xBF, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // Zero delta time.
+    0x0, 0x0,
+    // num missing packet ranges
+    0x01,
+    // missing packet delta
+    0x01,
+    // 0 more missing packets in range.
+    0x00,
+  };
+
+  scoped_ptr<QuicPacket> data(
+      framer_.BuildUnsizedDataPacket(header, frames).packet);
+  ASSERT_TRUE(data != NULL);
+
+  test::CompareCharArraysWithHexError("constructed packet",
+                                      data->data(), data->length(),
+                                      AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) {
+  if (version_ <= QUIC_VERSION_14) {
+    return;
+  }
+  QuicPacketHeader header;
+  header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+  header.public_header.reset_flag = false;
+  header.public_header.version_flag = false;
+  header.fec_flag = false;
+  header.entropy_flag = false;
+  header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+  header.fec_group = 0;
+
+  QuicCongestionFeedbackFrame congestion_feedback_frame;
+  congestion_feedback_frame.type = kTCP;
+  congestion_feedback_frame.tcp.receive_window = 0x4030;
+
+  QuicFrames frames;
+  frames.push_back(QuicFrame(&congestion_feedback_frame));
+
+  unsigned char packet[] = {
+    // public flags (8 byte guid)
+    0x3C,
+    // guid
+    0x10, 0x32, 0x54, 0x76,
+    0x98, 0xBA, 0xDC, 0xFE,
+    // packet sequence number
+    0xBC, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // private flags
+    0x00,
+
+    // frame type (congestion feedback frame)
+    0x20,
+    // congestion feedback type (TCP)
+    0x00,
+    // TCP receive window
+    0x03, 0x04,
+  };
+
+  scoped_ptr<QuicPacket> data(
+      framer_.BuildUnsizedDataPacket(header, frames).packet);
+  ASSERT_TRUE(data != NULL);
+
+  test::CompareCharArraysWithHexError("constructed packet",
+                                      data->data(), data->length(),
+                                      AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCPV14) {
+  if (version_ > QUIC_VERSION_14) {
+    return;
+  }
   QuicPacketHeader header;
   header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
   header.public_header.reset_flag = false;
   header.public_header.version_flag = false;
   header.fec_flag = false;
-  header.entropy_flag = true;
-  header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8);
+  header.entropy_flag = false;
+  header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
   header.fec_group = 0;
 
-  QuicAckFrame ack_frame;
-  ack_frame.received_info.entropy_hash = 0x43;
-  ack_frame.received_info.largest_observed = GG_UINT64_C(0x770123456789ABF);
-  ack_frame.received_info.delta_time_largest_observed = QuicTime::Delta::Zero();
-  ack_frame.received_info.missing_packets.insert(
-      GG_UINT64_C(0x770123456789ABE));
-  ack_frame.sent_info.entropy_hash = 0x14;
-  ack_frame.sent_info.least_unacked = GG_UINT64_C(0x770123456789AA0);
+  QuicCongestionFeedbackFrame congestion_feedback_frame;
+  congestion_feedback_frame.type = kTCP;
+  congestion_feedback_frame.tcp.receive_window = 0x4030;
 
   QuicFrames frames;
-  frames.push_back(QuicFrame(&ack_frame));
+  frames.push_back(QuicFrame(&congestion_feedback_frame));
 
   unsigned char packet[] = {
     // public flags (8 byte guid)
@@ -2755,32 +3786,19 @@ TEST_P(QuicFramerTest, BuildAckFramePacket) {
     0x10, 0x32, 0x54, 0x76,
     0x98, 0xBA, 0xDC, 0xFE,
     // packet sequence number
-    0xA8, 0x9A, 0x78, 0x56,
+    0xBC, 0x9A, 0x78, 0x56,
     0x34, 0x12,
-    // private flags (entropy)
-    0x01,
+    // private flags
+    0x00,
 
-    // frame type (ack frame)
-    // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
-    0x6C,
-    // entropy hash of sent packets till least awaiting - 1.
-    0x14,
-    // least packet sequence number awaiting an ack, delta from sequence number.
-    0x08, 0x00, 0x00, 0x00,
-    0x00, 0x00,
-    // entropy hash of all received packets.
-    0x43,
-    // largest observed packet sequence number
-    0xBF, 0x9A, 0x78, 0x56,
-    0x34, 0x12,
-    // Zero delta time.
-    0x0, 0x0,
-    // num missing packet ranges
-    0x01,
-    // missing packet delta
-    0x01,
-    // 0 more missing packets in range.
+    // frame type (congestion feedback frame)
+    0x20,
+    // congestion feedback type (TCP)
     0x00,
+    // accumulated number of lost packets
+    0x00, 0x00,
+    // TCP receive window
+    0x03, 0x04,
   };
 
   scoped_ptr<QuicPacket> data(
@@ -2792,7 +3810,10 @@ TEST_P(QuicFramerTest, BuildAckFramePacket) {
                                       AsChars(packet), arraysize(packet));
 }
 
-TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) {
+TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) {
+  if (version_ <= QUIC_VERSION_14) {
+    return;
+  }
   QuicPacketHeader header;
   header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
   header.public_header.reset_flag = false;
@@ -2802,13 +3823,22 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) {
   header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
   header.fec_group = 0;
 
-  QuicCongestionFeedbackFrame congestion_feedback_frame;
-  congestion_feedback_frame.type = kTCP;
-  congestion_feedback_frame.tcp.accumulated_number_of_lost_packets = 0x0201;
-  congestion_feedback_frame.tcp.receive_window = 0x4030;
-
+  QuicCongestionFeedbackFrame frame;
+  frame.type = kInterArrival;
+  frame.inter_arrival.received_packet_times.insert(
+      make_pair(GG_UINT64_C(0x0123456789ABA),
+                start_.Add(QuicTime::Delta::FromMicroseconds(
+                    GG_UINT64_C(0x07E1D2C3B4A59687)))));
+  frame.inter_arrival.received_packet_times.insert(
+      make_pair(GG_UINT64_C(0x0123456789ABB),
+                start_.Add(QuicTime::Delta::FromMicroseconds(
+                    GG_UINT64_C(0x07E1D2C3B4A59688)))));
+  frame.inter_arrival.received_packet_times.insert(
+      make_pair(GG_UINT64_C(0x0123456789ABD),
+                start_.Add(QuicTime::Delta::FromMicroseconds(
+                    GG_UINT64_C(0x07E1D2C3B4A59689)))));
   QuicFrames frames;
-  frames.push_back(QuicFrame(&congestion_feedback_frame));
+  frames.push_back(QuicFrame(&frame));
 
   unsigned char packet[] = {
     // public flags (8 byte guid)
@@ -2824,12 +3854,24 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) {
 
     // frame type (congestion feedback frame)
     0x20,
-    // congestion feedback type (TCP)
-    0x00,
-    // accumulated number of lost packets
-    0x01, 0x02,
-    // TCP receive window
-    0x03, 0x04,
+    // congestion feedback type (inter arrival)
+    0x01,
+    // num received packets
+    0x03,
+    // lowest sequence number
+    0xBA, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // receive time
+    0x87, 0x96, 0xA5, 0xB4,
+    0xC3, 0xD2, 0xE1, 0x07,
+    // sequence delta
+    0x01, 0x00,
+    // time delta
+    0x01, 0x00, 0x00, 0x00,
+    // sequence delta (skip one packet)
+    0x03, 0x00,
+    // time delta
+    0x02, 0x00, 0x00, 0x00,
   };
 
   scoped_ptr<QuicPacket> data(
@@ -2841,7 +3883,10 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) {
                                       AsChars(packet), arraysize(packet));
 }
 
-TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) {
+TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrivalV14) {
+  if (version_ > QUIC_VERSION_14) {
+    return;
+  }
   QuicPacketHeader header;
   header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
   header.public_header.reset_flag = false;
@@ -2853,7 +3898,6 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) {
 
   QuicCongestionFeedbackFrame frame;
   frame.type = kInterArrival;
-  frame.inter_arrival.accumulated_number_of_lost_packets = 0x0302;
   frame.inter_arrival.received_packet_times.insert(
       make_pair(GG_UINT64_C(0x0123456789ABA),
                 start_.Add(QuicTime::Delta::FromMicroseconds(
@@ -2886,7 +3930,7 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) {
     // congestion feedback type (inter arrival)
     0x01,
     // accumulated_number_of_lost_packets
-    0x02, 0x03,
+    0x00, 0x00,
     // num received packets
     0x03,
     // lowest sequence number
@@ -2978,12 +4022,76 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInvalidFeedback) {
   QuicFrames frames;
   frames.push_back(QuicFrame(&congestion_feedback_frame));
 
+  scoped_ptr<QuicPacket> data;
+  EXPECT_DFATAL(
+      data.reset(framer_.BuildUnsizedDataPacket(header, frames).packet),
+      "AppendQuicCongestionFeedbackFrame failed");
+  ASSERT_TRUE(data == NULL);
+}
+
+TEST_P(QuicFramerTest, BuildRstFramePacketVersion13) {
+  if (version_ > QUIC_VERSION_13) {
+    return;
+  }
+
+  QuicPacketHeader header;
+  header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+  header.public_header.reset_flag = false;
+  header.public_header.version_flag = false;
+  header.fec_flag = false;
+  header.entropy_flag = false;
+  header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+  header.fec_group = 0;
+
+  QuicRstStreamFrame rst_frame;
+  rst_frame.stream_id = 0x01020304;
+  rst_frame.error_code = static_cast<QuicRstStreamErrorCode>(0x05060708);
+  rst_frame.error_details = "because I can";
+
+  unsigned char packet[] = {
+    // public flags (8 byte guid)
+    0x3C,
+    // guid
+    0x10, 0x32, 0x54, 0x76,
+    0x98, 0xBA, 0xDC, 0xFE,
+    // packet sequence number
+    0xBC, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // private flags
+    0x00,
+
+    // frame type (rst stream frame)
+    0x01,
+    // stream id
+    0x04, 0x03, 0x02, 0x01,
+    // error code
+    0x08, 0x07, 0x06, 0x05,
+    // error details length
+    0x0d, 0x00,
+    // error details
+    'b',  'e',  'c',  'a',
+    'u',  's',  'e',  ' ',
+    'I',  ' ',  'c',  'a',
+    'n',
+  };
+
+  QuicFrames frames;
+  frames.push_back(QuicFrame(&rst_frame));
+
   scoped_ptr<QuicPacket> data(
       framer_.BuildUnsizedDataPacket(header, frames).packet);
-  ASSERT_TRUE(data == NULL);
+  ASSERT_TRUE(data != NULL);
+
+  test::CompareCharArraysWithHexError("constructed packet",
+                                      data->data(), data->length(),
+                                      AsChars(packet), arraysize(packet));
 }
 
-TEST_P(QuicFramerTest, BuildRstFramePacket) {
+TEST_P(QuicFramerTest, BuildRstFramePacketQuic) {
+  if (version_ <= QUIC_VERSION_13) {
+    return;
+  }
+
   QuicPacketHeader header;
   header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
   header.public_header.reset_flag = false;
@@ -2997,6 +4105,7 @@ TEST_P(QuicFramerTest, BuildRstFramePacket) {
   rst_frame.stream_id = 0x01020304;
   rst_frame.error_code = static_cast<QuicRstStreamErrorCode>(0x05060708);
   rst_frame.error_details = "because I can";
+  rst_frame.byte_offset = 0x0807060504030201;
 
   unsigned char packet[] = {
     // public flags (8 byte guid)
@@ -3014,6 +4123,9 @@ TEST_P(QuicFramerTest, BuildRstFramePacket) {
     0x01,
     // stream id
     0x04, 0x03, 0x02, 0x01,
+    // sent byte offset
+    0x01, 0x02, 0x03, 0x04,
+    0x05, 0x06, 0x07, 0x08,
     // error code
     0x08, 0x07, 0x06, 0x05,
     // error details length
@@ -3142,6 +4254,112 @@ TEST_P(QuicFramerTest, BuildGoAwayPacket) {
                                       AsChars(packet), arraysize(packet));
 }
 
+TEST_P(QuicFramerTest, BuildWindowUpdatePacket) {
+  QuicPacketHeader header;
+  header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+  header.public_header.reset_flag = false;
+  header.public_header.version_flag = false;
+  header.fec_flag = false;
+  header.entropy_flag = true;
+  header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+  header.fec_group = 0;
+
+  QuicWindowUpdateFrame window_update_frame;
+  window_update_frame.stream_id = 0x01020304;
+  window_update_frame.byte_offset = 0x1122334455667788;
+
+  QuicFrames frames;
+  frames.push_back(QuicFrame(&window_update_frame));
+
+  unsigned char packet[] = {
+    // public flags (8 byte guid)
+    0x3C,
+    // guid
+    0x10, 0x32, 0x54, 0x76,
+    0x98, 0xBA, 0xDC, 0xFE,
+    // packet sequence number
+    0xBC, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // private flags(entropy)
+    0x01,
+
+    // frame type (window update frame)
+    0x04,
+    // stream id
+    0x04, 0x03, 0x02, 0x01,
+    // byte offset
+    0x88, 0x77, 0x66, 0x55,
+    0x44, 0x33, 0x22, 0x11,
+  };
+
+  if (version_ > QUIC_VERSION_13) {
+    scoped_ptr<QuicPacket> data(
+        framer_.BuildUnsizedDataPacket(header, frames).packet);
+    ASSERT_TRUE(data != NULL);
+
+    test::CompareCharArraysWithHexError("constructed packet", data->data(),
+                                        data->length(), AsChars(packet),
+                                        arraysize(packet));
+  } else {
+    string expected_error = "Attempt to add a WindowUpdateFrame in " +
+                            QuicVersionToString(version_);
+    EXPECT_DFATAL(framer_.BuildUnsizedDataPacket(header, frames),
+                  expected_error);
+    return;
+  }
+}
+
+TEST_P(QuicFramerTest, BuildBlockedPacket) {
+  QuicPacketHeader header;
+  header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+  header.public_header.reset_flag = false;
+  header.public_header.version_flag = false;
+  header.fec_flag = false;
+  header.entropy_flag = true;
+  header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+  header.fec_group = 0;
+
+  QuicBlockedFrame blocked_frame;
+  blocked_frame.stream_id = 0x01020304;
+
+  QuicFrames frames;
+  frames.push_back(QuicFrame(&blocked_frame));
+
+  unsigned char packet[] = {
+    // public flags (8 byte guid)
+    0x3C,
+    // guid
+    0x10, 0x32, 0x54, 0x76,
+    0x98, 0xBA, 0xDC, 0xFE,
+    // packet sequence number
+    0xBC, 0x9A, 0x78, 0x56,
+    0x34, 0x12,
+    // private flags(entropy)
+    0x01,
+
+    // frame type (blocked frame)
+    0x05,
+    // stream id
+    0x04, 0x03, 0x02, 0x01,
+  };
+
+  if (version_ > QUIC_VERSION_13) {
+    scoped_ptr<QuicPacket> data(
+        framer_.BuildUnsizedDataPacket(header, frames).packet);
+    ASSERT_TRUE(data != NULL);
+
+    test::CompareCharArraysWithHexError("constructed packet", data->data(),
+                                        data->length(), AsChars(packet),
+                                        arraysize(packet));
+  } else {
+    string expected_error =
+        "Attempt to add a BlockedFrame in " + QuicVersionToString(version_);
+    EXPECT_DFATAL(framer_.BuildUnsizedDataPacket(header, frames),
+                  expected_error);
+    return;
+  }
+}
+
 TEST_P(QuicFramerTest, BuildPublicResetPacket) {
   QuicPublicResetPacket reset_packet;
   reset_packet.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
@@ -3152,15 +4370,79 @@ TEST_P(QuicFramerTest, BuildPublicResetPacket) {
 
   unsigned char packet[] = {
     // public flags (public reset, 8 byte GUID)
-    0x3E,
+    0x0E,
+    // guid
+    0x10, 0x32, 0x54, 0x76,
+    0x98, 0xBA, 0xDC, 0xFE,
+    // message tag (kPRST)
+    'P', 'R', 'S', 'T',
+    // num_entries (2) + padding
+    0x02, 0x00, 0x00, 0x00,
+    // tag kRNON
+    'R', 'N', 'O', 'N',
+    // end offset 8
+    0x08, 0x00, 0x00, 0x00,
+    // tag kRSEQ
+    'R', 'S', 'E', 'Q',
+    // end offset 16
+    0x10, 0x00, 0x00, 0x00,
+    // nonce proof
+    0x89, 0x67, 0x45, 0x23,
+    0x01, 0xEF, 0xCD, 0xAB,
+    // rejected sequence number
+    0xBC, 0x9A, 0x78, 0x56,
+    0x34, 0x12, 0x00, 0x00,
+  };
+
+  scoped_ptr<QuicEncryptedPacket> data(
+      framer_.BuildPublicResetPacket(reset_packet));
+  ASSERT_TRUE(data != NULL);
+
+  test::CompareCharArraysWithHexError("constructed packet",
+                                      data->data(), data->length(),
+                                      AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildPublicResetPacketWithClientAddress) {
+  QuicPublicResetPacket reset_packet;
+  reset_packet.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210);
+  reset_packet.public_header.reset_flag = true;
+  reset_packet.public_header.version_flag = false;
+  reset_packet.rejected_sequence_number = GG_UINT64_C(0x123456789ABC);
+  reset_packet.nonce_proof = GG_UINT64_C(0xABCDEF0123456789);
+  reset_packet.client_address = IPEndPoint(Loopback4(), 0x1234);
+
+  unsigned char packet[] = {
+    // public flags (public reset, 8 byte GUID)
+    0x0E,
     // guid
     0x10, 0x32, 0x54, 0x76,
     0x98, 0xBA, 0xDC, 0xFE,
+    // message tag (kPRST)
+    'P', 'R', 'S', 'T',
+    // num_entries (3) + padding
+    0x03, 0x00, 0x00, 0x00,
+    // tag kRNON
+    'R', 'N', 'O', 'N',
+    // end offset 8
+    0x08, 0x00, 0x00, 0x00,
+    // tag kRSEQ
+    'R', 'S', 'E', 'Q',
+    // end offset 16
+    0x10, 0x00, 0x00, 0x00,
+    // tag kCADR
+    'C', 'A', 'D', 'R',
+    // end offset 24
+    0x18, 0x00, 0x00, 0x00,
     // nonce proof
     0x89, 0x67, 0x45, 0x23,
     0x01, 0xEF, 0xCD, 0xAB,
     // rejected sequence number
     0xBC, 0x9A, 0x78, 0x56,
+    0x34, 0x12, 0x00, 0x00,
+    // client address
+    0x02, 0x00,
+    0x7F, 0x00, 0x00, 0x01,
     0x34, 0x12,
   };
 
@@ -3300,7 +4582,7 @@ TEST_P(QuicFramerTest, Truncation) {
 
   QuicAckFrame ack_frame;
   ack_frame.received_info.largest_observed = 601;
-  ack_frame.sent_info.least_unacked = 0;
+  ack_frame.sent_info.least_unacked = header.packet_sequence_number - 1;
   for (uint64 i = 1; i < ack_frame.received_info.largest_observed; i += 2) {
     ack_frame.received_info.missing_packets.insert(i);
   }
@@ -3324,7 +4606,8 @@ TEST_P(QuicFramerTest, Truncation) {
   ASSERT_TRUE(framer_.ProcessPacket(*ack_packet));
   ASSERT_EQ(1u, visitor_.ack_frames_.size());
   const QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0];
-  EXPECT_EQ(0u, processed_ack_frame.sent_info.least_unacked);
+  EXPECT_EQ(header.packet_sequence_number - 1,
+            processed_ack_frame.sent_info.least_unacked);
   EXPECT_TRUE(processed_ack_frame.received_info.is_truncated);
   EXPECT_EQ(510u, processed_ack_frame.received_info.largest_observed);
   ASSERT_EQ(255u, processed_ack_frame.received_info.missing_packets.size());
@@ -3348,7 +4631,7 @@ TEST_P(QuicFramerTest, CleanTruncation) {
 
   QuicAckFrame ack_frame;
   ack_frame.received_info.largest_observed = 201;
-  ack_frame.sent_info.least_unacked = 0;
+  ack_frame.sent_info.least_unacked = header.packet_sequence_number - 2;
   for (uint64 i = 1; i < ack_frame.received_info.largest_observed; ++i) {
     ack_frame.received_info.missing_packets.insert(i);
   }