Project import generated by Copybara.

PiperOrigin-RevId: 235760953
Change-Id: I302811f663a1f4772520f3fa3148e54851e0227a
diff --git a/http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc b/http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc
index bf928d3..0221328 100644
--- a/http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc
+++ b/http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc
@@ -96,10 +96,9 @@
   const uint32_t value_length_;
 };
 
-INSTANTIATE_TEST_CASE_P(VariousOriginAndValueLengths,
-                        AltSvcPayloadLengthTests,
-                        ::testing::Combine(::testing::Values(0, 1, 3, 65535),
-                                           ::testing::Values(0, 1, 3, 65537)));
+INSTANTIATE_TEST_SUITE_P(VariousOriginAndValueLengths, AltSvcPayloadLengthTests,
+                         ::testing::Combine(::testing::Values(0, 1, 3, 65535),
+                                            ::testing::Values(0, 1, 3, 65537)));
 
 TEST_P(AltSvcPayloadLengthTests, ValidOriginAndValueLength) {
   Http2String origin = Random().RandString(origin_length_);
diff --git a/http2/decoder/payload_decoders/continuation_payload_decoder_test.cc b/http2/decoder/payload_decoders/continuation_payload_decoder_test.cc
index 63888d3..6e81828 100644
--- a/http2/decoder/payload_decoders/continuation_payload_decoder_test.cc
+++ b/http2/decoder/payload_decoders/continuation_payload_decoder_test.cc
@@ -67,9 +67,8 @@
   const uint32_t length_;
 };
 
-INSTANTIATE_TEST_CASE_P(VariousLengths,
-                        ContinuationPayloadDecoderTest,
-                        ::testing::Values(0, 1, 2, 3, 4, 5, 6));
+INSTANTIATE_TEST_SUITE_P(VariousLengths, ContinuationPayloadDecoderTest,
+                         ::testing::Values(0, 1, 2, 3, 4, 5, 6));
 
 TEST_P(ContinuationPayloadDecoderTest, ValidLength) {
   Http2String hpack_payload = Random().RandString(length_);
diff --git a/http2/decoder/payload_decoders/data_payload_decoder_test.cc b/http2/decoder/payload_decoders/data_payload_decoder_test.cc
index e5253b9..0622f17 100644
--- a/http2/decoder/payload_decoders/data_payload_decoder_test.cc
+++ b/http2/decoder/payload_decoders/data_payload_decoder_test.cc
@@ -98,9 +98,8 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(VariousPadLengths,
-                        DataPayloadDecoderTest,
-                        ::testing::Values(0, 1, 2, 3, 4, 254, 255, 256));
+INSTANTIATE_TEST_SUITE_P(VariousPadLengths, DataPayloadDecoderTest,
+                         ::testing::Values(0, 1, 2, 3, 4, 254, 255, 256));
 
 TEST_P(DataPayloadDecoderTest, VariousDataPayloadSizes) {
   for (size_t data_size : {0, 1, 2, 3, 255, 256, 1024}) {
diff --git a/http2/decoder/payload_decoders/goaway_payload_decoder_test.cc b/http2/decoder/payload_decoders/goaway_payload_decoder_test.cc
index 1df5214..c90cdcc 100644
--- a/http2/decoder/payload_decoders/goaway_payload_decoder_test.cc
+++ b/http2/decoder/payload_decoders/goaway_payload_decoder_test.cc
@@ -83,9 +83,8 @@
   const uint32_t length_;
 };
 
-INSTANTIATE_TEST_CASE_P(VariousLengths,
-                        GoAwayOpaqueDataLengthTests,
-                        ::testing::Values(0, 1, 2, 3, 4, 5, 6));
+INSTANTIATE_TEST_SUITE_P(VariousLengths, GoAwayOpaqueDataLengthTests,
+                         ::testing::Values(0, 1, 2, 3, 4, 5, 6));
 
 TEST_P(GoAwayOpaqueDataLengthTests, ValidLength) {
   Http2GoAwayFields goaway;
diff --git a/http2/decoder/payload_decoders/headers_payload_decoder_test.cc b/http2/decoder/payload_decoders/headers_payload_decoder_test.cc
index a6fcb65..cb58b8e 100644
--- a/http2/decoder/payload_decoders/headers_payload_decoder_test.cc
+++ b/http2/decoder/payload_decoders/headers_payload_decoder_test.cc
@@ -94,9 +94,8 @@
                                                 HeadersPayloadDecoderPeer,
                                                 Listener> {};
 
-INSTANTIATE_TEST_CASE_P(VariousPadLengths,
-                        HeadersPayloadDecoderTest,
-                        ::testing::Values(0, 1, 2, 3, 4, 254, 255, 256));
+INSTANTIATE_TEST_SUITE_P(VariousPadLengths, HeadersPayloadDecoderTest,
+                         ::testing::Values(0, 1, 2, 3, 4, 254, 255, 256));
 
 // Decode various sizes of (fake) HPACK payload, both with and without the
 // PRIORITY flag set.
diff --git a/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc b/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc
index 9d55a80..94220f7 100644
--- a/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc
+++ b/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc
@@ -87,9 +87,8 @@
                                                 PushPromisePayloadDecoderPeer,
                                                 Listener> {};
 
-INSTANTIATE_TEST_CASE_P(VariousPadLengths,
-                        PushPromisePayloadDecoderTest,
-                        ::testing::Values(0, 1, 2, 3, 4, 254, 255, 256));
+INSTANTIATE_TEST_SUITE_P(VariousPadLengths, PushPromisePayloadDecoderTest,
+                         ::testing::Values(0, 1, 2, 3, 4, 254, 255, 256));
 
 // Payload contains the required Http2PushPromiseFields, followed by some
 // (fake) HPACK payload.
diff --git a/http2/decoder/payload_decoders/unknown_payload_decoder_test.cc b/http2/decoder/payload_decoders/unknown_payload_decoder_test.cc
index 7ba95f7..f9d203a 100644
--- a/http2/decoder/payload_decoders/unknown_payload_decoder_test.cc
+++ b/http2/decoder/payload_decoders/unknown_payload_decoder_test.cc
@@ -77,9 +77,8 @@
   const uint32_t length_;
 };
 
-INSTANTIATE_TEST_CASE_P(VariousLengths,
-                        UnknownPayloadDecoderTest,
-                        ::testing::Values(0, 1, 2, 3, 255, 256));
+INSTANTIATE_TEST_SUITE_P(VariousLengths, UnknownPayloadDecoderTest,
+                         ::testing::Values(0, 1, 2, 3, 255, 256));
 
 TEST_P(UnknownPayloadDecoderTest, ValidLength) {
   Http2String unknown_payload = Random().RandString(length_);
diff --git a/http2/hpack/decoder/hpack_decoder_test.cc b/http2/hpack/decoder/hpack_decoder_test.cc
index 563e977..83a0ede 100644
--- a/http2/hpack/decoder/hpack_decoder_test.cc
+++ b/http2/hpack/decoder/hpack_decoder_test.cc
@@ -224,7 +224,7 @@
   bool saw_start_ = false;
   bool saw_end_ = false;
 };
-INSTANTIATE_TEST_CASE_P(AllWays, HpackDecoderTest, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(AllWays, HpackDecoderTest, ::testing::Bool());
 
 // Test based on RFC 7541, section C.3: Request Examples without Huffman Coding.
 // This section shows several consecutive header lists, corresponding to HTTP
diff --git a/http2/hpack/decoder/hpack_entry_decoder_test.cc b/http2/hpack/decoder/hpack_entry_decoder_test.cc
index 7249bd5..b447e82 100644
--- a/http2/hpack/decoder/hpack_entry_decoder_test.cc
+++ b/http2/hpack/decoder/hpack_entry_decoder_test.cc
@@ -157,9 +157,8 @@
   const HpackEntryType entry_type_;
 };
 
-INSTANTIATE_TEST_CASE_P(
-    AllLiteralTypes,
-    HpackLiteralEntryDecoderTest,
+INSTANTIATE_TEST_SUITE_P(
+    AllLiteralTypes, HpackLiteralEntryDecoderTest,
     testing::Values(HpackEntryType::kIndexedLiteralHeader,
                     HpackEntryType::kUnindexedLiteralHeader,
                     HpackEntryType::kNeverIndexedLiteralHeader));
diff --git a/http2/hpack/huffman/hpack_huffman_transcoder_test.cc b/http2/hpack/huffman/hpack_huffman_transcoder_test.cc
index cfd73b5..2791d6a 100644
--- a/http2/hpack/huffman/hpack_huffman_transcoder_test.cc
+++ b/http2/hpack/huffman/hpack_huffman_transcoder_test.cc
@@ -139,9 +139,9 @@
   const char c_;
 };
 
-INSTANTIATE_TEST_CASE_P(HpackHuffmanTranscoderAdjacentCharTest,
-                        HpackHuffmanTranscoderAdjacentCharTest,
-                        ::testing::Range(0, 256));
+INSTANTIATE_TEST_SUITE_P(HpackHuffmanTranscoderAdjacentCharTest,
+                         HpackHuffmanTranscoderAdjacentCharTest,
+                         ::testing::Range(0, 256));
 
 // Test c_ adjacent to every other character, both before and after.
 TEST_P(HpackHuffmanTranscoderAdjacentCharTest, RoundTripAdjacentChar) {
@@ -169,11 +169,11 @@
   const size_t length_;
 };
 
-INSTANTIATE_TEST_CASE_P(
-    HpackHuffmanTranscoderRepeatedCharTest,
-    HpackHuffmanTranscoderRepeatedCharTest,
-    ::testing::Combine(::testing::Range(0, 256),
-                       ::testing::Values(1, 2, 3, 4, 8, 16, 32)));
+INSTANTIATE_TEST_SUITE_P(HpackHuffmanTranscoderRepeatedCharTest,
+                         HpackHuffmanTranscoderRepeatedCharTest,
+                         ::testing::Combine(::testing::Range(0, 256),
+                                            ::testing::Values(1, 2, 3, 4, 8, 16,
+                                                              32)));
 
 TEST_P(HpackHuffmanTranscoderRepeatedCharTest, RoundTripRepeatedChar) {
   ASSERT_TRUE(TranscodeAndValidateSeveralWays(MakeString()));
diff --git a/http2/hpack/varint/hpack_varint_decoder_test.cc b/http2/hpack/varint/hpack_varint_decoder_test.cc
index 32a4a30..2d589fd 100644
--- a/http2/hpack/varint/hpack_varint_decoder_test.cc
+++ b/http2/hpack/varint/hpack_varint_decoder_test.cc
@@ -107,9 +107,8 @@
   uint8_t prefix_length_;
 };
 
-INSTANTIATE_TEST_CASE_P(
-    HpackVarintDecoderTest,
-    HpackVarintDecoderTest,
+INSTANTIATE_TEST_SUITE_P(
+    HpackVarintDecoderTest, HpackVarintDecoderTest,
     ::testing::Combine(
         // Bits of the first byte not part of the prefix should be ignored.
         ::testing::Values(0b00000000, 0b11111111, 0b10101010),
diff --git a/http2/http2_structures_test.cc b/http2/http2_structures_test.cc
index 30ebb36..78107f7 100644
--- a/http2/http2_structures_test.cc
+++ b/http2/http2_structures_test.cc
@@ -163,10 +163,9 @@
 };
 
 class IsEndStreamTest : public Http2FrameHeaderTypeAndFlagTest {};
-INSTANTIATE_TEST_CASE_P(IsEndStream,
-                        IsEndStreamTest,
-                        Combine(ValuesIn(ValidFrameTypes()),
-                                Values(~Http2FrameFlag::END_STREAM, 0xff)));
+INSTANTIATE_TEST_SUITE_P(IsEndStream, IsEndStreamTest,
+                         Combine(ValuesIn(ValidFrameTypes()),
+                                 Values(~Http2FrameFlag::END_STREAM, 0xff)));
 TEST_P(IsEndStreamTest, IsEndStream) {
   const bool is_set =
       (flags_ & Http2FrameFlag::END_STREAM) == Http2FrameFlag::END_STREAM;
@@ -201,10 +200,9 @@
 }
 
 class IsACKTest : public Http2FrameHeaderTypeAndFlagTest {};
-INSTANTIATE_TEST_CASE_P(IsAck,
-                        IsACKTest,
-                        Combine(ValuesIn(ValidFrameTypes()),
-                                Values(~Http2FrameFlag::ACK, 0xff)));
+INSTANTIATE_TEST_SUITE_P(IsAck, IsACKTest,
+                         Combine(ValuesIn(ValidFrameTypes()),
+                                 Values(~Http2FrameFlag::ACK, 0xff)));
 TEST_P(IsACKTest, IsAck) {
   const bool is_set = (flags_ & Http2FrameFlag::ACK) == Http2FrameFlag::ACK;
   Http2String flags_string;
@@ -238,10 +236,9 @@
 }
 
 class IsEndHeadersTest : public Http2FrameHeaderTypeAndFlagTest {};
-INSTANTIATE_TEST_CASE_P(IsEndHeaders,
-                        IsEndHeadersTest,
-                        Combine(ValuesIn(ValidFrameTypes()),
-                                Values(~Http2FrameFlag::END_HEADERS, 0xff)));
+INSTANTIATE_TEST_SUITE_P(IsEndHeaders, IsEndHeadersTest,
+                         Combine(ValuesIn(ValidFrameTypes()),
+                                 Values(~Http2FrameFlag::END_HEADERS, 0xff)));
 TEST_P(IsEndHeadersTest, IsEndHeaders) {
   const bool is_set =
       (flags_ & Http2FrameFlag::END_HEADERS) == Http2FrameFlag::END_HEADERS;
@@ -279,10 +276,9 @@
 }
 
 class IsPaddedTest : public Http2FrameHeaderTypeAndFlagTest {};
-INSTANTIATE_TEST_CASE_P(IsPadded,
-                        IsPaddedTest,
-                        Combine(ValuesIn(ValidFrameTypes()),
-                                Values(~Http2FrameFlag::PADDED, 0xff)));
+INSTANTIATE_TEST_SUITE_P(IsPadded, IsPaddedTest,
+                         Combine(ValuesIn(ValidFrameTypes()),
+                                 Values(~Http2FrameFlag::PADDED, 0xff)));
 TEST_P(IsPaddedTest, IsPadded) {
   const bool is_set =
       (flags_ & Http2FrameFlag::PADDED) == Http2FrameFlag::PADDED;
@@ -318,10 +314,9 @@
 }
 
 class HasPriorityTest : public Http2FrameHeaderTypeAndFlagTest {};
-INSTANTIATE_TEST_CASE_P(HasPriority,
-                        HasPriorityTest,
-                        Combine(ValuesIn(ValidFrameTypes()),
-                                Values(~Http2FrameFlag::PRIORITY, 0xff)));
+INSTANTIATE_TEST_SUITE_P(HasPriority, HasPriorityTest,
+                         Combine(ValuesIn(ValidFrameTypes()),
+                                 Values(~Http2FrameFlag::PRIORITY, 0xff)));
 TEST_P(HasPriorityTest, HasPriority) {
   const bool is_set =
       (flags_ & Http2FrameFlag::PRIORITY) == Http2FrameFlag::PRIORITY;
diff --git a/quic/core/chlo_extractor.cc b/quic/core/chlo_extractor.cc
index b44aa9c..caf0af1 100644
--- a/quic/core/chlo_extractor.cc
+++ b/quic/core/chlo_extractor.cc
@@ -39,6 +39,7 @@
   bool OnUnauthenticatedHeader(const QuicPacketHeader& header) override;
   void OnDecryptedPacket(EncryptionLevel level) override {}
   bool OnPacketHeader(const QuicPacketHeader& header) override;
+  void OnCoalescedPacket(const QuicEncryptedPacket& packet) override;
   bool OnStreamFrame(const QuicStreamFrame& frame) override;
   bool OnCryptoFrame(const QuicCryptoFrame& frame) override;
   bool OnAckFrameStart(QuicPacketNumber largest_acked,
@@ -75,6 +76,9 @@
   void OnError(CryptoFramer* framer) override;
   void OnHandshakeMessage(const CryptoHandshakeMessage& message) override;
 
+  // Shared implementation between OnStreamFrame and OnCryptoFrame.
+  bool OnHandshakeData(QuicStringPiece data);
+
   bool found_chlo() { return found_chlo_; }
   bool chlo_contains_tags() { return chlo_contains_tags_; }
 
@@ -119,39 +123,56 @@
 bool ChloFramerVisitor::OnPacketHeader(const QuicPacketHeader& header) {
   return true;
 }
+void ChloFramerVisitor::OnCoalescedPacket(const QuicEncryptedPacket& packet) {}
 bool ChloFramerVisitor::OnStreamFrame(const QuicStreamFrame& frame) {
+  if (framer_->transport_version() >= QUIC_VERSION_47) {
+    // CHLO will be sent in CRYPTO frames in v47 and above.
+    return false;
+  }
   QuicStringPiece data(frame.data_buffer, frame.data_length);
   if (frame.stream_id ==
           QuicUtils::GetCryptoStreamId(framer_->transport_version()) &&
       frame.offset == 0 && QuicTextUtils::StartsWith(data, "CHLO")) {
-    CryptoFramer crypto_framer;
-    crypto_framer.set_visitor(this);
-    if (!crypto_framer.ProcessInput(data)) {
-      return false;
-    }
-    // Interrogate the crypto framer and see if there are any
-    // intersecting tags between what we saw in the maybe-CHLO and the
-    // indicator set.
-    for (const QuicTag tag : create_session_tag_indicators_) {
-      if (crypto_framer.HasTag(tag)) {
-        chlo_contains_tags_ = true;
-      }
-    }
-    if (chlo_contains_tags_ && delegate_) {
-      // Unfortunately, because this is a partial CHLO,
-      // OnHandshakeMessage was never called, so the ALPN was never
-      // extracted. Fake it up a bit and send it to the delegate so that
-      // the correct dispatch can happen.
-      crypto_framer.ForceHandshake();
-    }
+    return OnHandshakeData(data);
   }
-
   return true;
 }
 
 bool ChloFramerVisitor::OnCryptoFrame(const QuicCryptoFrame& frame) {
-  // TODO(nharper): Implement.
-  return false;
+  if (framer_->transport_version() < QUIC_VERSION_47) {
+    // CHLO will be in stream frames before v47.
+    return false;
+  }
+  QuicStringPiece data(frame.data_buffer, frame.data_length);
+  if (frame.offset == 0 && QuicTextUtils::StartsWith(data, "CHLO")) {
+    return OnHandshakeData(data);
+  }
+  return true;
+}
+
+bool ChloFramerVisitor::OnHandshakeData(QuicStringPiece data) {
+  CryptoFramer crypto_framer;
+  crypto_framer.set_visitor(this);
+  if (!crypto_framer.ProcessInput(data)) {
+    return false;
+  }
+  // Interrogate the crypto framer and see if there are any
+  // intersecting tags between what we saw in the maybe-CHLO and the
+  // indicator set.
+  for (const QuicTag tag : create_session_tag_indicators_) {
+    if (crypto_framer.HasTag(tag)) {
+      chlo_contains_tags_ = true;
+    }
+  }
+  if (chlo_contains_tags_ && delegate_) {
+    // Unfortunately, because this is a partial CHLO,
+    // OnHandshakeMessage was never called, so the ALPN was never
+    // extracted. Fake it up a bit and send it to the delegate so that
+    // the correct dispatch can happen.
+    crypto_framer.ForceHandshake();
+  }
+
+  return true;
 }
 
 bool ChloFramerVisitor::OnAckFrameStart(QuicPacketNumber /*largest_acked*/,
diff --git a/quic/core/chlo_extractor_test.cc b/quic/core/chlo_extractor_test.cc
index d16050b..e8b6483 100644
--- a/quic/core/chlo_extractor_test.cc
+++ b/quic/core/chlo_extractor_test.cc
@@ -53,14 +53,35 @@
     header_.reset_flag = false;
     header_.packet_number_length = PACKET_4BYTE_PACKET_NUMBER;
     header_.packet_number = QuicPacketNumber(1);
+    if (QuicVersionHasLongHeaderLengths(header_.version.transport_version)) {
+      header_.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1;
+      header_.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+    }
   }
 
-  void MakePacket(const QuicStreamFrame& stream_frame) {
-    QuicFrame frame(stream_frame);
+  void MakePacket(ParsedQuicVersion version,
+                  QuicStringPiece data,
+                  bool munge_offset,
+                  bool munge_stream_id) {
     QuicFrames frames;
-    frames.push_back(frame);
+    size_t offset = 0;
+    if (munge_offset) {
+      offset++;
+    }
     QuicFramer framer(SupportedVersions(header_.version), QuicTime::Zero(),
                       Perspective::IS_CLIENT);
+    if (version.transport_version < QUIC_VERSION_47 || munge_stream_id) {
+      QuicStreamId stream_id =
+          QuicUtils::GetCryptoStreamId(version.transport_version);
+      if (munge_stream_id) {
+        stream_id++;
+      }
+      frames.push_back(
+          QuicFrame(QuicStreamFrame(stream_id, false, offset, data)));
+    } else {
+      frames.push_back(
+          QuicFrame(new QuicCryptoFrame(ENCRYPTION_NONE, offset, data)));
+    }
     std::unique_ptr<QuicPacket> packet(
         BuildUnsizedDataPacket(&framer, header_, frames));
     EXPECT_TRUE(packet != nullptr);
@@ -70,6 +91,7 @@
     ASSERT_NE(0u, encrypted_length);
     packet_ = QuicMakeUnique<QuicEncryptedPacket>(buffer_, encrypted_length);
     EXPECT_TRUE(packet_ != nullptr);
+    DeleteFrames(&frames);
   }
 
  protected:
@@ -86,11 +108,19 @@
   QuicString client_hello_str(client_hello.GetSerialized().AsStringPiece());
   // Construct a CHLO with each supported version
   for (ParsedQuicVersion version : AllSupportedVersions()) {
+    SCOPED_TRACE(version);
     ParsedQuicVersionVector versions(SupportedVersions(version));
     header_.version = version;
-    MakePacket(
-        QuicStreamFrame(QuicUtils::GetCryptoStreamId(version.transport_version),
-                        false, 0, client_hello_str));
+    if (QuicVersionHasLongHeaderLengths(version.transport_version) &&
+        header_.version_flag) {
+      header_.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1;
+      header_.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+    } else {
+      header_.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_0;
+      header_.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_0;
+    }
+    MakePacket(version, client_hello_str, /*munge_offset*/ false,
+               /*munge_stream_id*/ false);
     EXPECT_TRUE(ChloExtractor::Extract(*packet_, versions, {}, &delegate_))
         << ParsedQuicVersionToString(version);
     EXPECT_EQ(version.transport_version, delegate_.transport_version());
@@ -105,10 +135,8 @@
   client_hello.set_tag(kCHLO);
 
   QuicString client_hello_str(client_hello.GetSerialized().AsStringPiece());
-  MakePacket(QuicStreamFrame(QuicUtils::GetCryptoStreamId(
-                                 AllSupportedVersions()[0].transport_version) +
-                                 1,
-                             false, 0, client_hello_str));
+  MakePacket(AllSupportedVersions()[0], client_hello_str,
+             /*munge_offset*/ false, /*munge_stream_id*/ true);
   EXPECT_FALSE(
       ChloExtractor::Extract(*packet_, AllSupportedVersions(), {}, &delegate_));
 }
@@ -118,17 +146,15 @@
   client_hello.set_tag(kCHLO);
 
   QuicString client_hello_str(client_hello.GetSerialized().AsStringPiece());
-  MakePacket(QuicStreamFrame(
-      QuicUtils::GetCryptoStreamId(AllSupportedVersions()[0].transport_version),
-      false, 1, client_hello_str));
+  MakePacket(AllSupportedVersions()[0], client_hello_str, /*munge_offset*/ true,
+             /*munge_stream_id*/ false);
   EXPECT_FALSE(
       ChloExtractor::Extract(*packet_, AllSupportedVersions(), {}, &delegate_));
 }
 
 TEST_F(ChloExtractorTest, DoesNotFindInvalidChlo) {
-  MakePacket(QuicStreamFrame(
-      QuicUtils::GetCryptoStreamId(AllSupportedVersions()[0].transport_version),
-      false, 0, "foo"));
+  MakePacket(AllSupportedVersions()[0], "foo", /*munge_offset*/ false,
+             /*munge_stream_id*/ true);
   EXPECT_FALSE(
       ChloExtractor::Extract(*packet_, AllSupportedVersions(), {}, &delegate_));
 }
diff --git a/quic/core/congestion_control/general_loss_algorithm.cc b/quic/core/congestion_control/general_loss_algorithm.cc
index 27a3ab8..8cd5e71 100644
--- a/quic/core/congestion_control/general_loss_algorithm.cc
+++ b/quic/core/congestion_control/general_loss_algorithm.cc
@@ -32,7 +32,7 @@
 GeneralLossAlgorithm::GeneralLossAlgorithm(LossDetectionType loss_type)
     : loss_detection_timeout_(QuicTime::Zero()),
       least_in_flight_(1),
-      faster_detect_loss_(GetQuicReloadableFlag(quic_faster_detect_loss)) {
+      packet_number_space_(NUM_PACKET_NUMBER_SPACES) {
   SetLossDetectionType(loss_type);
 }
 
@@ -64,16 +64,14 @@
     const AckedPacketVector& packets_acked,
     LostPacketVector* packets_lost) {
   loss_detection_timeout_ = QuicTime::Zero();
-  if (faster_detect_loss_ && !packets_acked.empty() &&
+  if (!packets_acked.empty() &&
       packets_acked.front().packet_number == least_in_flight_) {
     if (least_in_flight_ + packets_acked.size() - 1 == largest_newly_acked) {
       // Optimization for the case when no packet is missing.
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_faster_detect_loss, 1, 3);
       least_in_flight_ = largest_newly_acked + 1;
       largest_previously_acked_ = largest_newly_acked;
       return;
     }
-    QUIC_RELOADABLE_FLAG_COUNT_N(quic_faster_detect_loss, 2, 3);
     // There is hole in acked_packets, increment least_in_flight_ if possible.
     for (const auto& acked : packets_acked) {
       if (acked.packet_number != least_in_flight_) {
@@ -89,34 +87,29 @@
                max_rtt + (max_rtt >> reordering_shift_));
   QuicPacketNumber packet_number = unacked_packets.GetLeastUnacked();
   auto it = unacked_packets.begin();
-  if (faster_detect_loss_) {
-    if (least_in_flight_.IsInitialized() && least_in_flight_ >= packet_number) {
-      if (least_in_flight_ > unacked_packets.largest_sent_packet() + 1) {
-        QUIC_BUG << "least_in_flight: " << least_in_flight_
-                 << " is greater than largest_sent_packet + 1: "
-                 << unacked_packets.largest_sent_packet() + 1;
-      } else {
-        QUIC_RELOADABLE_FLAG_COUNT_N(quic_faster_detect_loss, 3, 3);
-        it += (least_in_flight_ - packet_number);
-        packet_number = least_in_flight_;
-      }
-    }
-    // Clear least_in_flight_.
-    least_in_flight_.Clear();
-  } else {
-    if (largest_lost_.IsInitialized() && largest_lost_ >= packet_number) {
-      if (largest_lost_ > unacked_packets.largest_sent_packet()) {
-        QUIC_BUG << "largest_lost: " << largest_lost_
-                 << " is greater than largest_sent_packet: "
-                 << unacked_packets.largest_sent_packet();
-      } else {
-        it += (largest_lost_ - packet_number + 1);
-        packet_number = largest_lost_ + 1;
-      }
+  if (least_in_flight_.IsInitialized() && least_in_flight_ >= packet_number) {
+    if (least_in_flight_ > unacked_packets.largest_sent_packet() + 1) {
+      QUIC_BUG << "least_in_flight: " << least_in_flight_
+               << " is greater than largest_sent_packet + 1: "
+               << unacked_packets.largest_sent_packet() + 1;
+    } else {
+      it += (least_in_flight_ - packet_number);
+      packet_number = least_in_flight_;
     }
   }
+  // Clear least_in_flight_.
+  least_in_flight_.Clear();
+  DCHECK(!unacked_packets.use_uber_loss_algorithm() ||
+         packet_number_space_ ==
+             unacked_packets.GetPacketNumberSpace(largest_newly_acked));
   for (; it != unacked_packets.end() && packet_number <= largest_newly_acked;
        ++it, ++packet_number) {
+    if (unacked_packets.use_uber_loss_algorithm() &&
+        unacked_packets.GetPacketNumberSpace(it->encryption_level) !=
+            packet_number_space_) {
+      // Skip packets of different packet number space.
+      continue;
+    }
     if (!it->in_flight) {
       continue;
     }
@@ -144,8 +137,18 @@
     // Only early retransmit(RFC5827) when the last packet gets acked and
     // there are retransmittable packets in flight.
     // This also implements a timer-protected variant of FACK.
-    if (unacked_packets.largest_sent_retransmittable_packet() <=
-            largest_newly_acked ||
+    QuicPacketNumber largest_sent_retransmittable_packet;
+    if (unacked_packets.use_uber_loss_algorithm()) {
+      // Use largest_sent_retransmittable_packet of corresponding packet number
+      // space for timer based loss detection.
+      largest_sent_retransmittable_packet =
+          unacked_packets.GetLargestSentRetransmittableOfPacketNumberSpace(
+              packet_number_space_);
+    } else {
+      largest_sent_retransmittable_packet =
+          unacked_packets.largest_sent_retransmittable_packet();
+    }
+    if (largest_sent_retransmittable_packet <= largest_newly_acked ||
         loss_type_ == kTime || loss_type_ == kAdaptiveTime) {
       QuicTime when_lost = it->sent_time + loss_delay;
       if (time < when_lost) {
@@ -176,11 +179,6 @@
     least_in_flight_ = largest_newly_acked + 1;
   }
   largest_previously_acked_ = largest_newly_acked;
-  if (!packets_lost->empty()) {
-    DCHECK(!largest_lost_.IsInitialized() ||
-           largest_lost_ < packets_lost->back().packet_number);
-    largest_lost_ = packets_lost->back().packet_number;
-  }
 }
 
 QuicTime GeneralLossAlgorithm::GetLossTimeout() const {
@@ -225,4 +223,14 @@
   } while (proposed_extra_time < extra_time_needed && reordering_shift_ > 0);
 }
 
+void GeneralLossAlgorithm::SetPacketNumberSpace(
+    PacketNumberSpace packet_number_space) {
+  if (packet_number_space_ < NUM_PACKET_NUMBER_SPACES) {
+    QUIC_BUG << "Cannot switch packet_number_space";
+    return;
+  }
+
+  packet_number_space_ = packet_number_space;
+}
+
 }  // namespace quic
diff --git a/quic/core/congestion_control/general_loss_algorithm.h b/quic/core/congestion_control/general_loss_algorithm.h
index 0d8e565..a2bcadd 100644
--- a/quic/core/congestion_control/general_loss_algorithm.h
+++ b/quic/core/congestion_control/general_loss_algorithm.h
@@ -55,6 +55,8 @@
       const RttStats& rtt_stats,
       QuicPacketNumber spurious_retransmission) override;
 
+  void SetPacketNumberSpace(PacketNumberSpace packet_number_space);
+
   int reordering_shift() const { return reordering_shift_; }
 
  private:
@@ -71,15 +73,11 @@
   int reordering_shift_;
   // The largest newly acked from the previous call to DetectLosses.
   QuicPacketNumber largest_previously_acked_;
-  // The largest lost packet.
-  // TODO(fayang): Remove this variable when deprecating
-  // quic_faster_detect_loss.
-  QuicPacketNumber largest_lost_;
   // The least in flight packet. Loss detection should start from this. Please
   // note, least_in_flight_ could be largest packet ever sent + 1.
   QuicPacketNumber least_in_flight_;
-  // Latched value of quic_faster_detect_loss flag.
-  const bool faster_detect_loss_;
+  // This is only used when quic_use_uber_loss_algorithm is true.
+  PacketNumberSpace packet_number_space_;
 };
 
 }  // namespace quic
diff --git a/quic/core/congestion_control/general_loss_algorithm_test.cc b/quic/core/congestion_control/general_loss_algorithm_test.cc
index 2cd19a7..91a25da 100644
--- a/quic/core/congestion_control/general_loss_algorithm_test.cc
+++ b/quic/core/congestion_control/general_loss_algorithm_test.cc
@@ -23,10 +23,13 @@
 
 class GeneralLossAlgorithmTest : public QuicTest {
  protected:
-  GeneralLossAlgorithmTest() {
+  GeneralLossAlgorithmTest() : unacked_packets_(Perspective::IS_CLIENT) {
     rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
                          QuicTime::Delta::Zero(), clock_.Now());
     EXPECT_LT(0, rtt_stats_.smoothed_rtt().ToMicroseconds());
+    if (unacked_packets_.use_uber_loss_algorithm()) {
+      loss_algorithm_.SetPacketNumberSpace(HANDSHAKE_DATA);
+    }
   }
 
   ~GeneralLossAlgorithmTest() override {}
@@ -54,9 +57,12 @@
   void VerifyLosses(uint64_t largest_newly_acked,
                     const AckedPacketVector& packets_acked,
                     const std::vector<uint64_t>& losses_expected) {
-    if (!unacked_packets_.largest_acked().IsInitialized() ||
-        QuicPacketNumber(largest_newly_acked) >
-            unacked_packets_.largest_acked()) {
+    if (unacked_packets_.use_uber_loss_algorithm()) {
+      unacked_packets_.MaybeUpdateLargestAckedOfPacketNumberSpace(
+          ENCRYPTION_NONE, QuicPacketNumber(largest_newly_acked));
+    } else if (!unacked_packets_.largest_acked().IsInitialized() ||
+               QuicPacketNumber(largest_newly_acked) >
+                   unacked_packets_.largest_acked()) {
       unacked_packets_.IncreaseLargestAcked(
           QuicPacketNumber(largest_newly_acked));
     }
@@ -208,7 +214,12 @@
   clock_.AdvanceTime(rtt_stats_.smoothed_rtt());
 
   // Early retransmit when the final packet gets acked and the first is nacked.
-  unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2));
+  if (unacked_packets_.use_uber_loss_algorithm()) {
+    unacked_packets_.MaybeUpdateLargestAckedOfPacketNumberSpace(
+        ENCRYPTION_NONE, QuicPacketNumber(2));
+  } else {
+    unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2));
+  }
   unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
   packets_acked.push_back(
       AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero()));
@@ -226,7 +237,12 @@
   clock_.AdvanceTime(rtt_stats_.smoothed_rtt());
 
   // Early retransmit when the final packet gets acked and the first is nacked.
-  unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2));
+  if (unacked_packets_.use_uber_loss_algorithm()) {
+    unacked_packets_.MaybeUpdateLargestAckedOfPacketNumberSpace(
+        ENCRYPTION_NONE, QuicPacketNumber(2));
+  } else {
+    unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2));
+  }
   unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
   packets_acked.push_back(
       AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero()));
@@ -253,7 +269,12 @@
   AckedPacketVector packets_acked;
   // Wait another RTT and ack 2.
   clock_.AdvanceTime(rtt_stats_.smoothed_rtt());
-  unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2));
+  if (unacked_packets_.use_uber_loss_algorithm()) {
+    unacked_packets_.MaybeUpdateLargestAckedOfPacketNumberSpace(
+        ENCRYPTION_NONE, QuicPacketNumber(2));
+  } else {
+    unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2));
+  }
   unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
   packets_acked.push_back(
       AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero()));
diff --git a/quic/core/congestion_control/send_algorithm_test.cc b/quic/core/congestion_control/send_algorithm_test.cc
index 6001690..d7bfd85 100644
--- a/quic/core/congestion_control/send_algorithm_test.cc
+++ b/quic/core/congestion_control/send_algorithm_test.cc
@@ -268,10 +268,9 @@
   SendAlgorithmInterface* sender_;
 };
 
-INSTANTIATE_TEST_CASE_P(SendAlgorithmTests,
-                        SendAlgorithmTest,
-                        ::testing::ValuesIn(GetTestParams()),
-                        TestParamToString);
+INSTANTIATE_TEST_SUITE_P(SendAlgorithmTests, SendAlgorithmTest,
+                         ::testing::ValuesIn(GetTestParams()),
+                         TestParamToString);
 
 // Test a simple long data transfer in the default setup.
 TEST_P(SendAlgorithmTest, SimpleWiredNetworkTransfer) {
diff --git a/quic/core/congestion_control/uber_loss_algorithm.cc b/quic/core/congestion_control/uber_loss_algorithm.cc
new file mode 100644
index 0000000..8db151b
--- /dev/null
+++ b/quic/core/congestion_control/uber_loss_algorithm.cc
@@ -0,0 +1,86 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.h"
+
+#include <algorithm>
+
+namespace quic {
+
+UberLossAlgorithm::UberLossAlgorithm() : UberLossAlgorithm(kNack) {}
+
+UberLossAlgorithm::UberLossAlgorithm(LossDetectionType loss_type)
+    : loss_type_(loss_type) {
+  SetLossDetectionType(loss_type);
+  for (int8_t i = INITIAL_DATA; i < NUM_PACKET_NUMBER_SPACES; ++i) {
+    general_loss_algorithms_[i].SetPacketNumberSpace(
+        static_cast<PacketNumberSpace>(i));
+  }
+}
+
+LossDetectionType UberLossAlgorithm::GetLossDetectionType() const {
+  return loss_type_;
+}
+
+void UberLossAlgorithm::SetLossDetectionType(LossDetectionType loss_type) {
+  loss_type_ = loss_type;
+  for (auto& loss_algorithm : general_loss_algorithms_) {
+    loss_algorithm.SetLossDetectionType(loss_type);
+  }
+}
+
+void UberLossAlgorithm::DetectLosses(
+    const QuicUnackedPacketMap& unacked_packets,
+    QuicTime time,
+    const RttStats& rtt_stats,
+    QuicPacketNumber /*largest_newly_acked*/,
+    const AckedPacketVector& packets_acked,
+    LostPacketVector* packets_lost) {
+  DCHECK(unacked_packets.use_uber_loss_algorithm());
+  for (int8_t i = INITIAL_DATA; i < NUM_PACKET_NUMBER_SPACES; ++i) {
+    const QuicPacketNumber largest_acked =
+        unacked_packets.GetLargestAckedOfPacketNumberSpace(
+            static_cast<PacketNumberSpace>(i));
+    if (!largest_acked.IsInitialized() ||
+        unacked_packets.GetLeastUnacked() > largest_acked) {
+      // Skip detecting losses if no packet has been received for this packet
+      // number space or the least_unacked is greater than largest_acked.
+      continue;
+    }
+
+    general_loss_algorithms_[i].DetectLosses(unacked_packets, time, rtt_stats,
+                                             largest_acked, packets_acked,
+                                             packets_lost);
+  }
+}
+
+QuicTime UberLossAlgorithm::GetLossTimeout() const {
+  QuicTime loss_timeout = QuicTime::Zero();
+  // Returns the earliest non-zero loss timeout.
+  for (int8_t i = INITIAL_DATA; i < NUM_PACKET_NUMBER_SPACES; ++i) {
+    const QuicTime timeout = general_loss_algorithms_[i].GetLossTimeout();
+    if (!loss_timeout.IsInitialized()) {
+      loss_timeout = timeout;
+      continue;
+    }
+    if (timeout.IsInitialized()) {
+      loss_timeout = std::min(loss_timeout, timeout);
+    }
+  }
+  return loss_timeout;
+}
+
+void UberLossAlgorithm::SpuriousRetransmitDetected(
+    const QuicUnackedPacketMap& unacked_packets,
+    QuicTime time,
+    const RttStats& rtt_stats,
+    QuicPacketNumber spurious_retransmission) {
+  DCHECK(unacked_packets.use_uber_loss_algorithm());
+  general_loss_algorithms_[unacked_packets.GetPacketNumberSpace(
+                               spurious_retransmission)]
+      .SpuriousRetransmitDetected(unacked_packets, time, rtt_stats,
+                                  spurious_retransmission);
+}
+
+}  // namespace quic
diff --git a/quic/core/congestion_control/uber_loss_algorithm.h b/quic/core/congestion_control/uber_loss_algorithm.h
new file mode 100644
index 0000000..dddbcb3
--- /dev/null
+++ b/quic/core/congestion_control/uber_loss_algorithm.h
@@ -0,0 +1,53 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_UBER_LOSS_ALGORITHM_H_
+#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_UBER_LOSS_ALGORITHM_H_
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.h"
+
+namespace quic {
+
+// This class comprises multiple loss algorithms, each per packet number space.
+class QUIC_EXPORT_PRIVATE UberLossAlgorithm : public LossDetectionInterface {
+ public:
+  UberLossAlgorithm();
+  explicit UberLossAlgorithm(LossDetectionType loss_type);
+  UberLossAlgorithm(const UberLossAlgorithm&) = delete;
+  UberLossAlgorithm& operator=(const UberLossAlgorithm&) = delete;
+  ~UberLossAlgorithm() override {}
+
+  LossDetectionType GetLossDetectionType() const override;
+
+  // Switches the loss detection type to |loss_type| and resets loss algorithm
+  // for all packet number spaces.
+  void SetLossDetectionType(LossDetectionType loss_type);
+
+  // Detects lost packets.
+  void DetectLosses(const QuicUnackedPacketMap& unacked_packets,
+                    QuicTime time,
+                    const RttStats& rtt_stats,
+                    QuicPacketNumber largest_newly_acked,
+                    const AckedPacketVector& packets_acked,
+                    LostPacketVector* packets_lost) override;
+
+  // Returns the earliest time the early retransmit timer should be active.
+  QuicTime GetLossTimeout() const override;
+
+  // Increases the loss detection threshold for time loss detection.
+  void SpuriousRetransmitDetected(
+      const QuicUnackedPacketMap& unacked_packets,
+      QuicTime time,
+      const RttStats& rtt_stats,
+      QuicPacketNumber spurious_retransmission) override;
+
+ private:
+  LossDetectionType loss_type_;
+  // One loss algorithm per packet number space.
+  GeneralLossAlgorithm general_loss_algorithms_[NUM_PACKET_NUMBER_SPACES];
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_CORE_CONGESTION_CONTROL_UBER_LOSS_ALGORITHM_H_
diff --git a/quic/core/congestion_control/uber_loss_algorithm_test.cc b/quic/core/congestion_control/uber_loss_algorithm_test.cc
new file mode 100644
index 0000000..9aa2041
--- /dev/null
+++ b/quic/core/congestion_control/uber_loss_algorithm_test.cc
@@ -0,0 +1,159 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.h"
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_unacked_packet_map_peer.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+// Default packet length.
+const uint32_t kDefaultLength = 1000;
+
+class UberLossAlgorithmTest : public QuicTest {
+ protected:
+  UberLossAlgorithmTest() {
+    SetQuicReloadableFlag(quic_use_uber_loss_algorithm, true);
+    unacked_packets_ =
+        QuicMakeUnique<QuicUnackedPacketMap>(Perspective::IS_CLIENT);
+    rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
+                         QuicTime::Delta::Zero(), clock_.Now());
+    EXPECT_LT(0, rtt_stats_.smoothed_rtt().ToMicroseconds());
+  }
+
+  void SendPacket(uint64_t packet_number, EncryptionLevel encryption_level) {
+    QuicStreamFrame frame;
+    frame.stream_id =
+        encryption_level == ENCRYPTION_NONE
+            ? QuicUtils::GetCryptoStreamId(
+                  CurrentSupportedVersions()[0].transport_version)
+            : QuicUtils::GetHeadersStreamId(
+                  CurrentSupportedVersions()[0].transport_version);
+    SerializedPacket packet(QuicPacketNumber(packet_number),
+                            PACKET_1BYTE_PACKET_NUMBER, nullptr, kDefaultLength,
+                            false, false);
+    packet.encryption_level = encryption_level;
+    packet.retransmittable_frames.push_back(QuicFrame(frame));
+    unacked_packets_->AddSentPacket(&packet, QuicPacketNumber(),
+                                    NOT_RETRANSMISSION, clock_.Now(), true);
+  }
+
+  void AckPackets(const std::vector<uint64_t>& packets_acked) {
+    packets_acked_.clear();
+    for (uint64_t acked : packets_acked) {
+      unacked_packets_->RemoveFromInFlight(QuicPacketNumber(acked));
+      packets_acked_.push_back(AckedPacket(QuicPacketNumber(acked),
+                                           kMaxPacketSize, QuicTime::Zero()));
+    }
+  }
+
+  void VerifyLosses(uint64_t largest_newly_acked,
+                    const AckedPacketVector& packets_acked,
+                    const std::vector<uint64_t>& losses_expected) {
+    LostPacketVector lost_packets;
+    loss_algorithm_.DetectLosses(*unacked_packets_, clock_.Now(), rtt_stats_,
+                                 QuicPacketNumber(largest_newly_acked),
+                                 packets_acked, &lost_packets);
+    ASSERT_EQ(losses_expected.size(), lost_packets.size());
+    for (size_t i = 0; i < losses_expected.size(); ++i) {
+      EXPECT_EQ(lost_packets[i].packet_number,
+                QuicPacketNumber(losses_expected[i]));
+    }
+  }
+
+  MockClock clock_;
+  std::unique_ptr<QuicUnackedPacketMap> unacked_packets_;
+  RttStats rtt_stats_;
+  UberLossAlgorithm loss_algorithm_;
+  AckedPacketVector packets_acked_;
+};
+
+TEST_F(UberLossAlgorithmTest, ScenarioA) {
+  // This test mimics a scenario: client sends 1-CHLO, 2-0RTT, 3-0RTT,
+  // timeout and retransmits 4-CHLO. Server acks packet 1 (ack gets lost).
+  // Server receives and buffers packets 2 and 3. Server receives packet 4 and
+  // processes handshake asynchronously, so server acks 4 and cannot process
+  // packets 2 and 3.
+  SendPacket(1, ENCRYPTION_NONE);
+  SendPacket(2, ENCRYPTION_ZERO_RTT);
+  SendPacket(3, ENCRYPTION_ZERO_RTT);
+  unacked_packets_->RemoveFromInFlight(QuicPacketNumber(1));
+  SendPacket(4, ENCRYPTION_NONE);
+
+  AckPackets({1, 4});
+  unacked_packets_->MaybeUpdateLargestAckedOfPacketNumberSpace(
+      ENCRYPTION_NONE, QuicPacketNumber(4));
+  // Verify no packet is detected lost.
+  VerifyLosses(4, packets_acked_, std::vector<uint64_t>{});
+  EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(UberLossAlgorithmTest, ScenarioB) {
+  // This test mimics a scenario: client sends 3-0RTT, 4-0RTT, receives SHLO,
+  // sends 5-1RTT, 6-1RTT.
+  SendPacket(3, ENCRYPTION_ZERO_RTT);
+  SendPacket(4, ENCRYPTION_ZERO_RTT);
+  SendPacket(5, ENCRYPTION_FORWARD_SECURE);
+  SendPacket(6, ENCRYPTION_FORWARD_SECURE);
+
+  AckPackets({4});
+  unacked_packets_->MaybeUpdateLargestAckedOfPacketNumberSpace(
+      ENCRYPTION_ZERO_RTT, QuicPacketNumber(4));
+  // No packet loss by acking 4.
+  VerifyLosses(4, packets_acked_, std::vector<uint64_t>{});
+  EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+
+  // Acking 6 causes 3 to be detected loss.
+  AckPackets({6});
+  unacked_packets_->MaybeUpdateLargestAckedOfPacketNumberSpace(
+      ENCRYPTION_FORWARD_SECURE, QuicPacketNumber(6));
+  VerifyLosses(6, packets_acked_, std::vector<uint64_t>{3});
+  EXPECT_EQ(clock_.Now() + 1.25 * rtt_stats_.smoothed_rtt(),
+            loss_algorithm_.GetLossTimeout());
+  packets_acked_.clear();
+
+  clock_.AdvanceTime(1.25 * rtt_stats_.latest_rtt());
+  // Verify 5 will be early retransmitted.
+  VerifyLosses(6, packets_acked_, {5});
+}
+
+TEST_F(UberLossAlgorithmTest, ScenarioC) {
+  // This test mimics a scenario: server sends 1-SHLO, 2-1RTT, 3-1RTT, 4-1RTT
+  // and retransmit 4-SHLO. Client receives and buffers packet 4. Client
+  // receives packet 5 and processes 4.
+  QuicUnackedPacketMapPeer::SetPerspective(unacked_packets_.get(),
+                                           Perspective::IS_SERVER);
+  SendPacket(1, ENCRYPTION_ZERO_RTT);
+  SendPacket(2, ENCRYPTION_FORWARD_SECURE);
+  SendPacket(3, ENCRYPTION_FORWARD_SECURE);
+  SendPacket(4, ENCRYPTION_FORWARD_SECURE);
+  unacked_packets_->RemoveFromInFlight(QuicPacketNumber(1));
+  SendPacket(5, ENCRYPTION_ZERO_RTT);
+
+  AckPackets({4, 5});
+  unacked_packets_->MaybeUpdateLargestAckedOfPacketNumberSpace(
+      ENCRYPTION_FORWARD_SECURE, QuicPacketNumber(4));
+  unacked_packets_->MaybeUpdateLargestAckedOfPacketNumberSpace(
+      ENCRYPTION_ZERO_RTT, QuicPacketNumber(5));
+  // No packet loss by acking 5.
+  VerifyLosses(5, packets_acked_, std::vector<uint64_t>{});
+  EXPECT_EQ(clock_.Now() + 1.25 * rtt_stats_.smoothed_rtt(),
+            loss_algorithm_.GetLossTimeout());
+  packets_acked_.clear();
+
+  clock_.AdvanceTime(1.25 * rtt_stats_.latest_rtt());
+  // Verify 2 and 3 will be early retransmitted.
+  VerifyLosses(5, packets_acked_, std::vector<uint64_t>{2, 3});
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace quic
diff --git a/quic/core/crypto/aead_base_decrypter.cc b/quic/core/crypto/aead_base_decrypter.cc
index 51cfa26..53bd0f0 100644
--- a/quic/core/crypto/aead_base_decrypter.cc
+++ b/quic/core/crypto/aead_base_decrypter.cc
@@ -141,8 +141,7 @@
   return true;
 }
 
-bool AeadBaseDecrypter::DecryptPacket(QuicTransportVersion /*version*/,
-                                      uint64_t packet_number,
+bool AeadBaseDecrypter::DecryptPacket(uint64_t packet_number,
                                       QuicStringPiece associated_data,
                                       QuicStringPiece ciphertext,
                                       char* output,
diff --git a/quic/core/crypto/aead_base_decrypter.h b/quic/core/crypto/aead_base_decrypter.h
index b4906b8..d374722 100644
--- a/quic/core/crypto/aead_base_decrypter.h
+++ b/quic/core/crypto/aead_base_decrypter.h
@@ -35,8 +35,7 @@
   bool SetIV(QuicStringPiece iv) override;
   bool SetPreliminaryKey(QuicStringPiece key) override;
   bool SetDiversificationNonce(const DiversificationNonce& nonce) override;
-  bool DecryptPacket(QuicTransportVersion version,
-                     uint64_t packet_number,
+  bool DecryptPacket(uint64_t packet_number,
                      QuicStringPiece associated_data,
                      QuicStringPiece ciphertext,
                      char* output,
diff --git a/quic/core/crypto/aead_base_encrypter.cc b/quic/core/crypto/aead_base_encrypter.cc
index 199851e..405292e 100644
--- a/quic/core/crypto/aead_base_encrypter.cc
+++ b/quic/core/crypto/aead_base_encrypter.cc
@@ -123,8 +123,7 @@
   return true;
 }
 
-bool AeadBaseEncrypter::EncryptPacket(QuicTransportVersion /*version*/,
-                                      uint64_t packet_number,
+bool AeadBaseEncrypter::EncryptPacket(uint64_t packet_number,
                                       QuicStringPiece associated_data,
                                       QuicStringPiece plaintext,
                                       char* output,
diff --git a/quic/core/crypto/aead_base_encrypter.h b/quic/core/crypto/aead_base_encrypter.h
index 255bd87..316d24e 100644
--- a/quic/core/crypto/aead_base_encrypter.h
+++ b/quic/core/crypto/aead_base_encrypter.h
@@ -33,8 +33,7 @@
   bool SetKey(QuicStringPiece key) override;
   bool SetNoncePrefix(QuicStringPiece nonce_prefix) override;
   bool SetIV(QuicStringPiece iv) override;
-  bool EncryptPacket(QuicTransportVersion version,
-                     uint64_t packet_number,
+  bool EncryptPacket(uint64_t packet_number,
                      QuicStringPiece associated_data,
                      QuicStringPiece plaintext,
                      char* output,
diff --git a/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc b/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc
index eb2fa5e..6513e45 100644
--- a/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc
+++ b/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc
@@ -212,8 +212,8 @@
   std::unique_ptr<char[]> output(new char[ciphertext.length()]);
   size_t output_length = 0;
   const bool success = decrypter->DecryptPacket(
-      QuicTransportVersionMax(), packet_number, associated_data, ciphertext,
-      output.get(), &output_length, ciphertext.length());
+      packet_number, associated_data, ciphertext, output.get(), &output_length,
+      ciphertext.length());
   if (!success) {
     return nullptr;
   }
diff --git a/quic/core/crypto/aes_128_gcm_decrypter_test.cc b/quic/core/crypto/aes_128_gcm_decrypter_test.cc
index ef66204..300e8a2 100644
--- a/quic/core/crypto/aes_128_gcm_decrypter_test.cc
+++ b/quic/core/crypto/aes_128_gcm_decrypter_test.cc
@@ -206,9 +206,9 @@
   decrypter->SetIV(nonce);
   std::unique_ptr<char[]> output(new char[ciphertext.length()]);
   size_t output_length = 0;
-  const bool success = decrypter->DecryptPacket(
-      QuicTransportVersionMax(), 0, associated_data, ciphertext, output.get(),
-      &output_length, ciphertext.length());
+  const bool success =
+      decrypter->DecryptPacket(0, associated_data, ciphertext, output.get(),
+                               &output_length, ciphertext.length());
   if (!success) {
     return nullptr;
   }
diff --git a/quic/core/crypto/aes_128_gcm_encrypter_test.cc b/quic/core/crypto/aes_128_gcm_encrypter_test.cc
index 79276ca..959cb9d 100644
--- a/quic/core/crypto/aes_128_gcm_encrypter_test.cc
+++ b/quic/core/crypto/aes_128_gcm_encrypter_test.cc
@@ -233,8 +233,8 @@
   Aes128GcmEncrypter encrypter;
   ASSERT_TRUE(encrypter.SetKey(key));
   ASSERT_TRUE(encrypter.SetIV(iv));
-  ASSERT_TRUE(encrypter.EncryptPacket(QUIC_VERSION_43, packet_num, aad, pt,
-                                      out.data(), &out_size, out.size()));
+  ASSERT_TRUE(encrypter.EncryptPacket(packet_num, aad, pt, out.data(),
+                                      &out_size, out.size()));
   EXPECT_EQ(out_size, out.size());
   test::CompareCharArraysWithHexError("ciphertext", out.data(), out.size(),
                                       ct.data(), ct.size());
diff --git a/quic/core/crypto/aes_256_gcm_decrypter_test.cc b/quic/core/crypto/aes_256_gcm_decrypter_test.cc
index b25b66b..cb5f702 100644
--- a/quic/core/crypto/aes_256_gcm_decrypter_test.cc
+++ b/quic/core/crypto/aes_256_gcm_decrypter_test.cc
@@ -210,9 +210,9 @@
   decrypter->SetIV(nonce);
   std::unique_ptr<char[]> output(new char[ciphertext.length()]);
   size_t output_length = 0;
-  const bool success = decrypter->DecryptPacket(
-      QuicTransportVersionMax(), 0, associated_data, ciphertext, output.get(),
-      &output_length, ciphertext.length());
+  const bool success =
+      decrypter->DecryptPacket(0, associated_data, ciphertext, output.get(),
+                               &output_length, ciphertext.length());
   if (!success) {
     return nullptr;
   }
diff --git a/quic/core/crypto/chacha20_poly1305_decrypter_test.cc b/quic/core/crypto/chacha20_poly1305_decrypter_test.cc
index a0c3cbe..5f5ae01 100644
--- a/quic/core/crypto/chacha20_poly1305_decrypter_test.cc
+++ b/quic/core/crypto/chacha20_poly1305_decrypter_test.cc
@@ -125,8 +125,8 @@
   std::unique_ptr<char[]> output(new char[ciphertext.length()]);
   size_t output_length = 0;
   const bool success = decrypter->DecryptPacket(
-      QuicTransportVersionMax(), packet_number, associated_data, ciphertext,
-      output.get(), &output_length, ciphertext.length());
+      packet_number, associated_data, ciphertext, output.get(), &output_length,
+      ciphertext.length());
   if (!success) {
     return nullptr;
   }
diff --git a/quic/core/crypto/chacha20_poly1305_encrypter_test.cc b/quic/core/crypto/chacha20_poly1305_encrypter_test.cc
index 8d9a900..d8c27bf 100644
--- a/quic/core/crypto/chacha20_poly1305_encrypter_test.cc
+++ b/quic/core/crypto/chacha20_poly1305_encrypter_test.cc
@@ -101,14 +101,14 @@
   QuicString plaintext = "plaintext";
   char encrypted[1024];
   size_t len;
-  ASSERT_TRUE(encrypter.EncryptPacket(QuicTransportVersionMax(), packet_number,
-                                      associated_data, plaintext, encrypted,
-                                      &len, QUIC_ARRAYSIZE(encrypted)));
+  ASSERT_TRUE(encrypter.EncryptPacket(packet_number, associated_data, plaintext,
+                                      encrypted, &len,
+                                      QUIC_ARRAYSIZE(encrypted)));
   QuicStringPiece ciphertext(encrypted, len);
   char decrypted[1024];
-  ASSERT_TRUE(decrypter.DecryptPacket(QuicTransportVersionMax(), packet_number,
-                                      associated_data, ciphertext, decrypted,
-                                      &len, QUIC_ARRAYSIZE(decrypted)));
+  ASSERT_TRUE(decrypter.DecryptPacket(packet_number, associated_data,
+                                      ciphertext, decrypted, &len,
+                                      QUIC_ARRAYSIZE(decrypted)));
 }
 
 TEST_F(ChaCha20Poly1305EncrypterTest, Encrypt) {
diff --git a/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc b/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc
index 824c2dd..dd74539 100644
--- a/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc
+++ b/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc
@@ -119,9 +119,9 @@
   decrypter->SetIV(nonce);
   std::unique_ptr<char[]> output(new char[ciphertext.length()]);
   size_t output_length = 0;
-  const bool success = decrypter->DecryptPacket(
-      QuicTransportVersionMax(), 0, associated_data, ciphertext, output.get(),
-      &output_length, ciphertext.length());
+  const bool success =
+      decrypter->DecryptPacket(0, associated_data, ciphertext, output.get(),
+                               &output_length, ciphertext.length());
   if (!success) {
     return nullptr;
   }
diff --git a/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc b/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc
index bc7c6c4..905472b 100644
--- a/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc
+++ b/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc
@@ -100,14 +100,14 @@
   QuicString plaintext = "plaintext";
   char encrypted[1024];
   size_t len;
-  ASSERT_TRUE(encrypter.EncryptPacket(QuicTransportVersionMax(), packet_number,
-                                      associated_data, plaintext, encrypted,
-                                      &len, QUIC_ARRAYSIZE(encrypted)));
+  ASSERT_TRUE(encrypter.EncryptPacket(packet_number, associated_data, plaintext,
+                                      encrypted, &len,
+                                      QUIC_ARRAYSIZE(encrypted)));
   QuicStringPiece ciphertext(encrypted, len);
   char decrypted[1024];
-  ASSERT_TRUE(decrypter.DecryptPacket(QuicTransportVersionMax(), packet_number,
-                                      associated_data, ciphertext, decrypted,
-                                      &len, QUIC_ARRAYSIZE(decrypted)));
+  ASSERT_TRUE(decrypter.DecryptPacket(packet_number, associated_data,
+                                      ciphertext, decrypted, &len,
+                                      QUIC_ARRAYSIZE(decrypted)));
 }
 
 TEST_F(ChaCha20Poly1305TlsEncrypterTest, Encrypt) {
diff --git a/quic/core/crypto/crypto_message_printer_bin.cc b/quic/core/crypto/crypto_message_printer_bin.cc
index f70c1c2..f14abb0 100644
--- a/quic/core/crypto/crypto_message_printer_bin.cc
+++ b/quic/core/crypto/crypto_message_printer_bin.cc
@@ -1,3 +1,7 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
 // Dumps the contents of a QUIC crypto handshake message in a human readable
 // format.
 //
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index 5795119..b6a1e90 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -261,9 +261,6 @@
 // Universal tags
 const QuicTag kPAD  = TAG('P', 'A', 'D', '\0');  // Padding
 
-// Server push tags
-const QuicTag kSPSH = TAG('S', 'P', 'S', 'H');  // Support server push.
-
 // Stats collection tags
 const QuicTag kEPID = TAG('E', 'P', 'I', 'D');  // Endpoint identifier.
 
diff --git a/quic/core/crypto/crypto_server_test.cc b/quic/core/crypto/crypto_server_test.cc
index c2c4041..4ed169e 100644
--- a/quic/core/crypto/crypto_server_test.cc
+++ b/quic/core/crypto/crypto_server_test.cc
@@ -236,7 +236,7 @@
 
   void ShouldSucceed(const CryptoHandshakeMessage& message) {
     bool called = false;
-    QuicSocketAddress server_address;
+    QuicSocketAddress server_address(QuicIpAddress::Any4(), 5);
     config_.ValidateClientHello(
         message, client_address_.host(), server_address,
         supported_versions_.front().transport_version, &clock_, signed_config_,
@@ -254,7 +254,7 @@
   void ShouldFailMentioning(const char* error_substr,
                             const CryptoHandshakeMessage& message,
                             bool* called) {
-    QuicSocketAddress server_address;
+    QuicSocketAddress server_address(QuicIpAddress::Any4(), 5);
     config_.ValidateClientHello(
         message, client_address_.host(), server_address,
         supported_versions_.front().transport_version, &clock_, signed_config_,
@@ -312,7 +312,7 @@
       QuicReferenceCountedPointer<ValidateCallback::Result> result,
       bool should_succeed,
       const char* error_substr) {
-    QuicSocketAddress server_address;
+    QuicSocketAddress server_address(QuicIpAddress::Any4(), 5);
     QuicConnectionId server_designated_connection_id =
         TestConnectionId(rand_for_id_generation_.RandUint64());
     bool called;
@@ -415,9 +415,8 @@
   std::unique_ptr<CryptoHandshakeMessage> server_config_;
 };
 
-INSTANTIATE_TEST_CASE_P(CryptoServerTests,
-                        CryptoServerTest,
-                        ::testing::ValuesIn(GetTestParams()));
+INSTANTIATE_TEST_SUITE_P(CryptoServerTests, CryptoServerTest,
+                         ::testing::ValuesIn(GetTestParams()));
 
 TEST_P(CryptoServerTest, BadSNI) {
   // clang-format off
diff --git a/quic/core/crypto/crypto_utils.cc b/quic/core/crypto/crypto_utils.cc
index 46d172f..4f6a61d 100644
--- a/quic/core/crypto/crypto_utils.cc
+++ b/quic/core/crypto/crypto_utils.cc
@@ -107,24 +107,10 @@
   std::vector<uint8_t> handshake_secret;
   handshake_secret.resize(EVP_MAX_MD_SIZE);
   size_t handshake_secret_len;
-  bool hkdf_extract_success;
-  if (!QuicConnectionIdSupportsVariableLength(perspective)) {
-    uint64_t connection_id64 = QuicConnectionIdToUInt64(connection_id);
-    uint8_t connection_id_bytes[sizeof(connection_id64)];
-    for (size_t i = 0; i < sizeof(connection_id64); ++i) {
-      connection_id_bytes[i] =
-          (connection_id64 >> ((sizeof(connection_id64) - i - 1) * 8)) & 0xff;
-    }
-    hkdf_extract_success =
-        HKDF_extract(handshake_secret.data(), &handshake_secret_len, hash,
-                     connection_id_bytes, QUIC_ARRAYSIZE(connection_id_bytes),
-                     kInitialSalt, QUIC_ARRAYSIZE(kInitialSalt));
-  } else {
-    hkdf_extract_success = HKDF_extract(
-        handshake_secret.data(), &handshake_secret_len, hash,
-        reinterpret_cast<const uint8_t*>(connection_id.data()),
-        connection_id.length(), kInitialSalt, QUIC_ARRAYSIZE(kInitialSalt));
-  }
+  const bool hkdf_extract_success = HKDF_extract(
+      handshake_secret.data(), &handshake_secret_len, hash,
+      reinterpret_cast<const uint8_t*>(connection_id.data()),
+      connection_id.length(), kInitialSalt, QUIC_ARRAYSIZE(kInitialSalt));
   QUIC_BUG_IF(!hkdf_extract_success)
       << "HKDF_extract failed when creating initial crypters";
   handshake_secret.resize(handshake_secret_len);
diff --git a/quic/core/crypto/null_decrypter.cc b/quic/core/crypto/null_decrypter.cc
index 0c108b6..288d3c4 100644
--- a/quic/core/crypto/null_decrypter.cc
+++ b/quic/core/crypto/null_decrypter.cc
@@ -38,8 +38,7 @@
   return true;
 }
 
-bool NullDecrypter::DecryptPacket(QuicTransportVersion version,
-                                  uint64_t /*packet_number*/,
+bool NullDecrypter::DecryptPacket(uint64_t /*packet_number*/,
                                   QuicStringPiece associated_data,
                                   QuicStringPiece ciphertext,
                                   char* output,
@@ -58,7 +57,7 @@
     QUIC_BUG << "Output buffer must be larger than the plaintext.";
     return false;
   }
-  if (hash != ComputeHash(version, associated_data, plaintext)) {
+  if (hash != ComputeHash(associated_data, plaintext)) {
     return false;
   }
   // Copy the plaintext to output.
@@ -97,21 +96,15 @@
   return true;
 }
 
-QuicUint128 NullDecrypter::ComputeHash(QuicTransportVersion version,
-                                       const QuicStringPiece data1,
+QuicUint128 NullDecrypter::ComputeHash(const QuicStringPiece data1,
                                        const QuicStringPiece data2) const {
   QuicUint128 correct_hash;
-  if (version > QUIC_VERSION_35) {
-    if (perspective_ == Perspective::IS_CLIENT) {
-      // Peer is a server.
-      correct_hash = QuicUtils::FNV1a_128_Hash_Three(data1, data2, "Server");
-
-    } else {
-      // Peer is a client.
-      correct_hash = QuicUtils::FNV1a_128_Hash_Three(data1, data2, "Client");
-    }
+  if (perspective_ == Perspective::IS_CLIENT) {
+    // Peer is a server.
+    correct_hash = QuicUtils::FNV1a_128_Hash_Three(data1, data2, "Server");
   } else {
-    correct_hash = QuicUtils::FNV1a_128_Hash_Two(data1, data2);
+    // Peer is a client.
+    correct_hash = QuicUtils::FNV1a_128_Hash_Three(data1, data2, "Client");
   }
   QuicUint128 mask = MakeQuicUint128(UINT64_C(0x0), UINT64_C(0xffffffff));
   mask <<= 96;
diff --git a/quic/core/crypto/null_decrypter.h b/quic/core/crypto/null_decrypter.h
index 3996eb7..8381987 100644
--- a/quic/core/crypto/null_decrypter.h
+++ b/quic/core/crypto/null_decrypter.h
@@ -35,8 +35,7 @@
   bool SetIV(QuicStringPiece iv) override;
   bool SetPreliminaryKey(QuicStringPiece key) override;
   bool SetDiversificationNonce(const DiversificationNonce& nonce) override;
-  bool DecryptPacket(QuicTransportVersion version,
-                     uint64_t packet_number,
+  bool DecryptPacket(uint64_t packet_number,
                      QuicStringPiece associated_data,
                      QuicStringPiece ciphertext,
                      char* output,
@@ -51,9 +50,7 @@
 
  private:
   bool ReadHash(QuicDataReader* reader, QuicUint128* hash);
-  QuicUint128 ComputeHash(QuicTransportVersion version,
-                          QuicStringPiece data1,
-                          QuicStringPiece data2) const;
+  QuicUint128 ComputeHash(QuicStringPiece data1, QuicStringPiece data2) const;
 
   Perspective perspective_;
 };
diff --git a/quic/core/crypto/null_decrypter_test.cc b/quic/core/crypto/null_decrypter_test.cc
index dd62a5c..09b1aaf 100644
--- a/quic/core/crypto/null_decrypter_test.cc
+++ b/quic/core/crypto/null_decrypter_test.cc
@@ -15,18 +15,35 @@
 TEST_F(NullDecrypterTest, DecryptClient) {
   unsigned char expected[] = {
       // fnv hash
-      0x97, 0xdc, 0x27, 0x2f, 0x18, 0xa8, 0x56, 0x73, 0xdf, 0x8d, 0x1d, 0xd0,
+      0x97,
+      0xdc,
+      0x27,
+      0x2f,
+      0x18,
+      0xa8,
+      0x56,
+      0x73,
+      0xdf,
+      0x8d,
+      0x1d,
+      0xd0,
       // payload
-      'g', 'o', 'o', 'd', 'b', 'y', 'e', '!',
+      'g',
+      'o',
+      'o',
+      'd',
+      'b',
+      'y',
+      'e',
+      '!',
   };
   const char* data = reinterpret_cast<const char*>(expected);
   size_t len = QUIC_ARRAYSIZE(expected);
   NullDecrypter decrypter(Perspective::IS_SERVER);
   char buffer[256];
   size_t length = 0;
-  ASSERT_TRUE(decrypter.DecryptPacket(QUIC_VERSION_39, 0, "hello world!",
-                                      QuicStringPiece(data, len), buffer,
-                                      &length, 256));
+  ASSERT_TRUE(decrypter.DecryptPacket(
+      0, "hello world!", QuicStringPiece(data, len), buffer, &length, 256));
   EXPECT_LT(0u, length);
   EXPECT_EQ("goodbye!", QuicStringPiece(buffer, length));
 }
@@ -34,56 +51,35 @@
 TEST_F(NullDecrypterTest, DecryptServer) {
   unsigned char expected[] = {
       // fnv hash
-      0x63, 0x5e, 0x08, 0x03, 0x32, 0x80, 0x8f, 0x73, 0xdf, 0x8d, 0x1d, 0x1a,
+      0x63,
+      0x5e,
+      0x08,
+      0x03,
+      0x32,
+      0x80,
+      0x8f,
+      0x73,
+      0xdf,
+      0x8d,
+      0x1d,
+      0x1a,
       // payload
-      'g', 'o', 'o', 'd', 'b', 'y', 'e', '!',
+      'g',
+      'o',
+      'o',
+      'd',
+      'b',
+      'y',
+      'e',
+      '!',
   };
   const char* data = reinterpret_cast<const char*>(expected);
   size_t len = QUIC_ARRAYSIZE(expected);
   NullDecrypter decrypter(Perspective::IS_CLIENT);
   char buffer[256];
   size_t length = 0;
-  ASSERT_TRUE(decrypter.DecryptPacket(QUIC_VERSION_39, 0, "hello world!",
-                                      QuicStringPiece(data, len), buffer,
-                                      &length, 256));
-  EXPECT_LT(0u, length);
-  EXPECT_EQ("goodbye!", QuicStringPiece(buffer, length));
-}
-
-TEST_F(NullDecrypterTest, DecryptClientPre37) {
-  unsigned char expected[] = {
-      // fnv hash
-      0xa0, 0x6f, 0x44, 0x8a, 0x44, 0xf8, 0x18, 0x3b, 0x47, 0x91, 0xb2, 0x13,
-      // payload
-      'g', 'o', 'o', 'd', 'b', 'y', 'e', '!',
-  };
-  const char* data = reinterpret_cast<const char*>(expected);
-  size_t len = QUIC_ARRAYSIZE(expected);
-  NullDecrypter decrypter(Perspective::IS_CLIENT);
-  char buffer[256];
-  size_t length = 0;
-  ASSERT_TRUE(decrypter.DecryptPacket(QUIC_VERSION_35, 0, "hello world!",
-                                      QuicStringPiece(data, len), buffer,
-                                      &length, 256));
-  EXPECT_LT(0u, length);
-  EXPECT_EQ("goodbye!", QuicStringPiece(buffer, length));
-}
-
-TEST_F(NullDecrypterTest, DecryptServerPre37) {
-  unsigned char expected[] = {
-      // fnv hash
-      0xa0, 0x6f, 0x44, 0x8a, 0x44, 0xf8, 0x18, 0x3b, 0x47, 0x91, 0xb2, 0x13,
-      // payload
-      'g', 'o', 'o', 'd', 'b', 'y', 'e', '!',
-  };
-  const char* data = reinterpret_cast<const char*>(expected);
-  size_t len = QUIC_ARRAYSIZE(expected);
-  NullDecrypter decrypter(Perspective::IS_SERVER);
-  char buffer[256];
-  size_t length = 0;
-  ASSERT_TRUE(decrypter.DecryptPacket(QUIC_VERSION_35, 0, "hello world!",
-                                      QuicStringPiece(data, len), buffer,
-                                      &length, 256));
+  ASSERT_TRUE(decrypter.DecryptPacket(
+      0, "hello world!", QuicStringPiece(data, len), buffer, &length, 256));
   EXPECT_LT(0u, length);
   EXPECT_EQ("goodbye!", QuicStringPiece(buffer, length));
 }
@@ -91,18 +87,35 @@
 TEST_F(NullDecrypterTest, BadHash) {
   unsigned char expected[] = {
       // fnv hash
-      0x46, 0x11, 0xea, 0x5f, 0xcf, 0x1d, 0x66, 0x5b, 0xba, 0xf0, 0xbc, 0xfd,
+      0x46,
+      0x11,
+      0xea,
+      0x5f,
+      0xcf,
+      0x1d,
+      0x66,
+      0x5b,
+      0xba,
+      0xf0,
+      0xbc,
+      0xfd,
       // payload
-      'g', 'o', 'o', 'd', 'b', 'y', 'e', '!',
+      'g',
+      'o',
+      'o',
+      'd',
+      'b',
+      'y',
+      'e',
+      '!',
   };
   const char* data = reinterpret_cast<const char*>(expected);
   size_t len = QUIC_ARRAYSIZE(expected);
   NullDecrypter decrypter(Perspective::IS_CLIENT);
   char buffer[256];
   size_t length = 0;
-  ASSERT_FALSE(decrypter.DecryptPacket(QUIC_VERSION_35, 0, "hello world!",
-                                       QuicStringPiece(data, len), buffer,
-                                       &length, 256));
+  ASSERT_FALSE(decrypter.DecryptPacket(
+      0, "hello world!", QuicStringPiece(data, len), buffer, &length, 256));
 }
 
 TEST_F(NullDecrypterTest, ShortInput) {
@@ -115,9 +128,8 @@
   NullDecrypter decrypter(Perspective::IS_CLIENT);
   char buffer[256];
   size_t length = 0;
-  ASSERT_FALSE(decrypter.DecryptPacket(QUIC_VERSION_35, 0, "hello world!",
-                                       QuicStringPiece(data, len), buffer,
-                                       &length, 256));
+  ASSERT_FALSE(decrypter.DecryptPacket(
+      0, "hello world!", QuicStringPiece(data, len), buffer, &length, 256));
 }
 
 }  // namespace test
diff --git a/quic/core/crypto/null_encrypter.cc b/quic/core/crypto/null_encrypter.cc
index e4a165f..9819a31 100644
--- a/quic/core/crypto/null_encrypter.cc
+++ b/quic/core/crypto/null_encrypter.cc
@@ -26,8 +26,7 @@
   return iv.empty();
 }
 
-bool NullEncrypter::EncryptPacket(QuicTransportVersion version,
-                                  uint64_t /*packet_number*/,
+bool NullEncrypter::EncryptPacket(uint64_t /*packet_number*/,
                                   QuicStringPiece associated_data,
                                   QuicStringPiece plaintext,
                                   char* output,
@@ -38,16 +37,12 @@
     return false;
   }
   QuicUint128 hash;
-  if (version > QUIC_VERSION_35) {
-    if (perspective_ == Perspective::IS_SERVER) {
-      hash =
-          QuicUtils::FNV1a_128_Hash_Three(associated_data, plaintext, "Server");
-    } else {
-      hash =
-          QuicUtils::FNV1a_128_Hash_Three(associated_data, plaintext, "Client");
-    }
+  if (perspective_ == Perspective::IS_SERVER) {
+    hash =
+        QuicUtils::FNV1a_128_Hash_Three(associated_data, plaintext, "Server");
   } else {
-    hash = QuicUtils::FNV1a_128_Hash_Two(associated_data, plaintext);
+    hash =
+        QuicUtils::FNV1a_128_Hash_Three(associated_data, plaintext, "Client");
   }
   // TODO(ianswett): memmove required for in place encryption.  Placing the
   // hash at the end would allow use of memcpy, doing nothing for in place.
diff --git a/quic/core/crypto/null_encrypter.h b/quic/core/crypto/null_encrypter.h
index 01ecd45..fe4487d 100644
--- a/quic/core/crypto/null_encrypter.h
+++ b/quic/core/crypto/null_encrypter.h
@@ -29,8 +29,7 @@
   bool SetKey(QuicStringPiece key) override;
   bool SetNoncePrefix(QuicStringPiece nonce_prefix) override;
   bool SetIV(QuicStringPiece iv) override;
-  bool EncryptPacket(QuicTransportVersion version,
-                     uint64_t packet_number,
+  bool EncryptPacket(uint64_t packet_number,
                      QuicStringPiece associated_data,
                      QuicStringPiece plaintext,
                      char* output,
diff --git a/quic/core/crypto/null_encrypter_test.cc b/quic/core/crypto/null_encrypter_test.cc
index 6dd4c98..fd95cc6 100644
--- a/quic/core/crypto/null_encrypter_test.cc
+++ b/quic/core/crypto/null_encrypter_test.cc
@@ -15,16 +15,33 @@
 TEST_F(NullEncrypterTest, EncryptClient) {
   unsigned char expected[] = {
       // fnv hash
-      0x97, 0xdc, 0x27, 0x2f, 0x18, 0xa8, 0x56, 0x73, 0xdf, 0x8d, 0x1d, 0xd0,
+      0x97,
+      0xdc,
+      0x27,
+      0x2f,
+      0x18,
+      0xa8,
+      0x56,
+      0x73,
+      0xdf,
+      0x8d,
+      0x1d,
+      0xd0,
       // payload
-      'g', 'o', 'o', 'd', 'b', 'y', 'e', '!',
+      'g',
+      'o',
+      'o',
+      'd',
+      'b',
+      'y',
+      'e',
+      '!',
   };
   char encrypted[256];
   size_t encrypted_len = 0;
   NullEncrypter encrypter(Perspective::IS_CLIENT);
-  ASSERT_TRUE(encrypter.EncryptPacket(QUIC_VERSION_39, 0, "hello world!",
-                                      "goodbye!", encrypted, &encrypted_len,
-                                      256));
+  ASSERT_TRUE(encrypter.EncryptPacket(0, "hello world!", "goodbye!", encrypted,
+                                      &encrypted_len, 256));
   test::CompareCharArraysWithHexError(
       "encrypted data", encrypted, encrypted_len,
       reinterpret_cast<const char*>(expected), QUIC_ARRAYSIZE(expected));
@@ -33,52 +50,33 @@
 TEST_F(NullEncrypterTest, EncryptServer) {
   unsigned char expected[] = {
       // fnv hash
-      0x63, 0x5e, 0x08, 0x03, 0x32, 0x80, 0x8f, 0x73, 0xdf, 0x8d, 0x1d, 0x1a,
+      0x63,
+      0x5e,
+      0x08,
+      0x03,
+      0x32,
+      0x80,
+      0x8f,
+      0x73,
+      0xdf,
+      0x8d,
+      0x1d,
+      0x1a,
       // payload
-      'g', 'o', 'o', 'd', 'b', 'y', 'e', '!',
+      'g',
+      'o',
+      'o',
+      'd',
+      'b',
+      'y',
+      'e',
+      '!',
   };
   char encrypted[256];
   size_t encrypted_len = 0;
   NullEncrypter encrypter(Perspective::IS_SERVER);
-  ASSERT_TRUE(encrypter.EncryptPacket(QUIC_VERSION_39, 0, "hello world!",
-                                      "goodbye!", encrypted, &encrypted_len,
-                                      256));
-  test::CompareCharArraysWithHexError(
-      "encrypted data", encrypted, encrypted_len,
-      reinterpret_cast<const char*>(expected), QUIC_ARRAYSIZE(expected));
-}
-
-TEST_F(NullEncrypterTest, EncryptClientPre37) {
-  unsigned char expected[] = {
-      // fnv hash
-      0xa0, 0x6f, 0x44, 0x8a, 0x44, 0xf8, 0x18, 0x3b, 0x47, 0x91, 0xb2, 0x13,
-      // payload
-      'g', 'o', 'o', 'd', 'b', 'y', 'e', '!',
-  };
-  char encrypted[256];
-  size_t encrypted_len = 0;
-  NullEncrypter encrypter(Perspective::IS_CLIENT);
-  ASSERT_TRUE(encrypter.EncryptPacket(QUIC_VERSION_35, 0, "hello world!",
-                                      "goodbye!", encrypted, &encrypted_len,
-                                      256));
-  test::CompareCharArraysWithHexError(
-      "encrypted data", encrypted, encrypted_len,
-      reinterpret_cast<const char*>(expected), QUIC_ARRAYSIZE(expected));
-}
-
-TEST_F(NullEncrypterTest, EncryptServerPre37) {
-  unsigned char expected[] = {
-      // fnv hash
-      0xa0, 0x6f, 0x44, 0x8a, 0x44, 0xf8, 0x18, 0x3b, 0x47, 0x91, 0xb2, 0x13,
-      // payload
-      'g', 'o', 'o', 'd', 'b', 'y', 'e', '!',
-  };
-  char encrypted[256];
-  size_t encrypted_len = 0;
-  NullEncrypter encrypter(Perspective::IS_SERVER);
-  ASSERT_TRUE(encrypter.EncryptPacket(QUIC_VERSION_35, 0, "hello world!",
-                                      "goodbye!", encrypted, &encrypted_len,
-                                      256));
+  ASSERT_TRUE(encrypter.EncryptPacket(0, "hello world!", "goodbye!", encrypted,
+                                      &encrypted_len, 256));
   test::CompareCharArraysWithHexError(
       "encrypted data", encrypted, encrypted_len,
       reinterpret_cast<const char*>(expected), QUIC_ARRAYSIZE(expected));
diff --git a/quic/core/crypto/quic_crypto_client_config.cc b/quic/core/crypto/quic_crypto_client_config.cc
index 56447bb..0bf1b41 100644
--- a/quic/core/crypto/quic_crypto_client_config.cc
+++ b/quic/core/crypto/quic_crypto_client_config.cc
@@ -657,14 +657,7 @@
     const QuicData& client_hello_serialized = out->GetSerialized();
     hkdf_input.append(QuicCryptoConfig::kCETVLabel,
                       strlen(QuicCryptoConfig::kCETVLabel) + 1);
-    if (!QuicConnectionIdSupportsVariableLength(Perspective::IS_CLIENT)) {
-      const uint64_t connection_id64_net =
-          QuicEndian::HostToNet64(QuicConnectionIdToUInt64(connection_id));
-      hkdf_input.append(reinterpret_cast<const char*>(&connection_id64_net),
-                        sizeof(connection_id64_net));
-    } else {
       hkdf_input.append(connection_id.data(), connection_id.length());
-    }
     hkdf_input.append(client_hello_serialized.data(),
                       client_hello_serialized.length());
     hkdf_input.append(cached->server_config());
@@ -696,8 +689,7 @@
     std::unique_ptr<char[]> output(new char[encrypted_len]);
     size_t output_size = 0;
     if (!crypters.encrypter->EncryptPacket(
-            preferred_version.transport_version, 0 /* packet number */,
-            QuicStringPiece() /* associated data */,
+            0 /* packet number */, QuicStringPiece() /* associated data */,
             cetv_plaintext.AsStringPiece(), output.get(), &output_size,
             encrypted_len)) {
       *error_details = "Packet encryption failed";
@@ -715,16 +707,8 @@
   //   out_params->hkdf_input_suffix
   //   out_params->initial_crypters
   out_params->hkdf_input_suffix.clear();
-  if (!QuicConnectionIdSupportsVariableLength(Perspective::IS_CLIENT)) {
-    const uint64_t connection_id64_net =
-        QuicEndian::HostToNet64(QuicConnectionIdToUInt64(connection_id));
-    out_params->hkdf_input_suffix.append(
-        reinterpret_cast<const char*>(&connection_id64_net),
-        sizeof(connection_id64_net));
-  } else {
     out_params->hkdf_input_suffix.append(connection_id.data(),
                                          connection_id.length());
-  }
   const QuicData& client_hello_serialized = out->GetSerialized();
   out_params->hkdf_input_suffix.append(client_hello_serialized.data(),
                                        client_hello_serialized.length());
@@ -857,15 +841,7 @@
 
   if (rej.tag() == kSREJ) {
     QuicConnectionId connection_id;
-    if (!QuicConnectionIdSupportsVariableLength(Perspective::IS_CLIENT)) {
-      uint64_t connection_id64;
-      if (rej.GetUint64(kRCID, &connection_id64) != QUIC_NO_ERROR) {
-        *error_details = "Missing kRCID";
-        return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
-      }
-      connection_id64 = QuicEndian::NetToHost64(connection_id64);
-      connection_id = QuicConnectionIdFromUInt64(connection_id64);
-    } else {
+
       QuicStringPiece connection_id_bytes;
       if (!rej.GetStringPiece(kRCID, &connection_id_bytes)) {
         *error_details = "Missing kRCID";
@@ -880,7 +856,6 @@
         *error_details = "Bad kRCID length";
         return QUIC_CRYPTO_INTERNAL_ERROR;
       }
-    }
     cached->add_server_designated_connection_id(connection_id);
     if (!nonce.empty()) {
       cached->add_server_nonce(QuicString(nonce));
diff --git a/quic/core/crypto/quic_crypto_server_config.cc b/quic/core/crypto/quic_crypto_server_config.cc
index 851f8e3..af67166 100644
--- a/quic/core/crypto/quic_crypto_server_config.cc
+++ b/quic/core/crypto/quic_crypto_server_config.cc
@@ -77,7 +77,7 @@
 
   std::unique_ptr<KeyExchange> Create(QuicString /*server_config_id*/,
                                       QuicTag type,
-                                      QuicStringPiece private_key) {
+                                      QuicStringPiece private_key) override {
     if (private_key.empty()) {
       QUIC_LOG(WARNING) << "Server config contains key exchange method without "
                            "corresponding private key: "
@@ -876,10 +876,6 @@
       << "ProcessClientHelloAfterGetProof: attempted to use connection ID "
       << connection_id << " which is invalid with version "
       << QuicVersionToString(version.transport_version);
-  if (!QuicConnectionIdSupportsVariableLength(Perspective::IS_SERVER)) {
-    connection_id = QuicConnectionIdFromUInt64(
-        QuicEndian::HostToNet64(QuicConnectionIdToUInt64(connection_id)));
-  }
   ProcessClientHelloHelper helper(&done_cb);
 
   if (found_error) {
@@ -1020,21 +1016,10 @@
 
   QuicString hkdf_suffix;
   const QuicData& client_hello_serialized = client_hello.GetSerialized();
-  if (!QuicConnectionIdSupportsVariableLength(Perspective::IS_SERVER)) {
-    // connection_id is already passed in in network byte order.
-    const uint64_t connection_id64_net =
-        QuicConnectionIdToUInt64(connection_id);
-    hkdf_suffix.reserve(sizeof(connection_id64_net) +
-                        client_hello_serialized.length() +
-                        requested_config->serialized.size());
-    hkdf_suffix.append(reinterpret_cast<const char*>(&connection_id64_net),
-                       sizeof(connection_id64_net));
-  } else {
     hkdf_suffix.reserve(connection_id.length() +
                         client_hello_serialized.length() +
                         requested_config->serialized.size());
     hkdf_suffix.append(connection_id.data(), connection_id.length());
-  }
   hkdf_suffix.append(client_hello_serialized.data(),
                      client_hello_serialized.length());
   hkdf_suffix.append(requested_config->serialized);
@@ -1057,15 +1042,7 @@
     QuicString hkdf_input;
     hkdf_input.append(QuicCryptoConfig::kCETVLabel,
                       strlen(QuicCryptoConfig::kCETVLabel) + 1);
-    if (!QuicConnectionIdSupportsVariableLength(Perspective::IS_SERVER)) {
-      // connection_id is already passed in in network byte order.
-      const uint64_t connection_id64_net =
-          QuicConnectionIdToUInt64(connection_id);
-      hkdf_input.append(reinterpret_cast<const char*>(&connection_id64_net),
-                        sizeof(connection_id64_net));
-    } else {
-      hkdf_input.append(connection_id.data(), connection_id.length());
-    }
+    hkdf_input.append(connection_id.data(), connection_id.length());
     hkdf_input.append(client_hello_copy_serialized.data(),
                       client_hello_copy_serialized.length());
     hkdf_input.append(requested_config->serialized);
@@ -1084,9 +1061,8 @@
     char plaintext[kMaxPacketSize];
     size_t plaintext_length = 0;
     const bool success = crypters.decrypter->DecryptPacket(
-        QUIC_VERSION_35, 0 /* packet number */,
-        QuicStringPiece() /* associated data */, cetv_ciphertext, plaintext,
-        &plaintext_length, kMaxPacketSize);
+        0 /* packet number */, QuicStringPiece() /* associated data */,
+        cetv_ciphertext, plaintext, &plaintext_length, kMaxPacketSize);
     if (!success) {
       helper.Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
                   "CETV decryption failure");
@@ -1613,10 +1589,6 @@
                   << "with server-designated connection ID "
                   << server_designated_connection_id;
     out->set_tag(kSREJ);
-    if (!QuicConnectionIdSupportsVariableLength(Perspective::IS_SERVER)) {
-      out->SetValue(kRCID, QuicEndian::HostToNet64(QuicConnectionIdToUInt64(
-                               server_designated_connection_id)));
-    } else {
       if (!QuicUtils::IsConnectionIdValidForVersion(
               server_designated_connection_id, version)) {
         QUIC_BUG << "Tried to send server designated connection ID "
@@ -1628,7 +1600,6 @@
       out->SetStringPiece(
           kRCID, QuicStringPiece(server_designated_connection_id.data(),
                                  server_designated_connection_id.length()));
-    }
   } else {
     out->set_tag(kREJ);
   }
@@ -2078,6 +2049,7 @@
 
 bool QuicCryptoServerConfig::IsNextConfigReady(QuicWallTime now) const {
   if (GetQuicReloadableFlag(quic_fix_config_rotation)) {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_fix_config_rotation);
     return !next_config_promotion_time_.IsZero() &&
            !next_config_promotion_time_.IsAfter(now);
   }
diff --git a/quic/core/crypto/quic_decrypter.h b/quic/core/crypto/quic_decrypter.h
index 892504c..c7c2ccc 100644
--- a/quic/core/crypto/quic_decrypter.h
+++ b/quic/core/crypto/quic_decrypter.h
@@ -53,8 +53,7 @@
   // to form the nonce.
   // TODO(wtc): add a way for DecryptPacket to report decryption failure due
   // to non-authentic inputs, as opposed to other reasons for failure.
-  virtual bool DecryptPacket(QuicTransportVersion version,
-                             uint64_t packet_number,
+  virtual bool DecryptPacket(uint64_t packet_number,
                              QuicStringPiece associated_data,
                              QuicStringPiece ciphertext,
                              char* output,
diff --git a/quic/core/crypto/quic_encrypter.h b/quic/core/crypto/quic_encrypter.h
index 591021e..cb53e33 100644
--- a/quic/core/crypto/quic_encrypter.h
+++ b/quic/core/crypto/quic_encrypter.h
@@ -34,8 +34,7 @@
   // SetNoncePrefix() to form the nonce. |output| must not overlap with
   // |associated_data|. If |output| overlaps with |plaintext| then
   // |plaintext| must be <= |output|.
-  virtual bool EncryptPacket(QuicTransportVersion version,
-                             uint64_t packet_number,
+  virtual bool EncryptPacket(uint64_t packet_number,
                              QuicStringPiece associated_data,
                              QuicStringPiece plaintext,
                              char* output,
diff --git a/quic/core/frames/quic_ack_frame.cc b/quic/core/frames/quic_ack_frame.cc
index 5f47b3f..389f1c0 100644
--- a/quic/core/frames/quic_ack_frame.cc
+++ b/quic/core/frames/quic_ack_frame.cc
@@ -5,9 +5,9 @@
 #include "net/third_party/quiche/src/quic/core/frames/quic_ack_frame.h"
 
 #include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
-#include "net/third_party/quiche/src/quic/platform/api/quic_interval.h"
 
 namespace quic {
 
diff --git a/quic/core/frames/quic_ack_frame.h b/quic/core/frames/quic_ack_frame.h
index 4dcfc3d..771d93e 100644
--- a/quic/core/frames/quic_ack_frame.h
+++ b/quic/core/frames/quic_ack_frame.h
@@ -7,11 +7,11 @@
 
 #include <ostream>
 
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
 #include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
-#include "net/third_party/quiche/src/quic/platform/api/quic_interval.h"
 
 namespace quic {
 
diff --git a/quic/core/frames/quic_frames_test.cc b/quic/core/frames/quic_frames_test.cc
index af9c40b..8099ccc 100644
--- a/quic/core/frames/quic_frames_test.cc
+++ b/quic/core/frames/quic_frames_test.cc
@@ -14,8 +14,8 @@
 #include "net/third_party/quiche/src/quic/core/frames/quic_stop_waiting_frame.h"
 #include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h"
 #include "net/third_party/quiche/src/quic/core/frames/quic_window_update_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
-#include "net/third_party/quiche/src/quic/platform/api/quic_interval.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
 
diff --git a/quic/core/frames/quic_message_frame.cc b/quic/core/frames/quic_message_frame.cc
index 79a23fe..c0de272 100644
--- a/quic/core/frames/quic_message_frame.cc
+++ b/quic/core/frames/quic_message_frame.cc
@@ -9,17 +9,20 @@
 
 namespace quic {
 
-QuicMessageFrame::QuicMessageFrame() : message_id(0) {}
+QuicMessageFrame::QuicMessageFrame()
+    : message_id(0), data(nullptr), message_length(0) {}
 
-QuicMessageFrame::QuicMessageFrame(QuicMessageId message_id,
-                                   QuicStringPiece message_data)
-    : message_id(message_id), message_data(message_data) {}
+QuicMessageFrame::QuicMessageFrame(QuicMessageId message_id)
+    : message_id(message_id), data(nullptr), message_length(0) {}
+
+QuicMessageFrame::QuicMessageFrame(const char* data, QuicPacketLength length)
+    : message_id(0), data(data), message_length(length) {}
 
 QuicMessageFrame::~QuicMessageFrame() {}
 
 std::ostream& operator<<(std::ostream& os, const QuicMessageFrame& s) {
   os << " message_id: " << s.message_id
-     << ", message_length: " << s.message_data.length() << " }\n";
+     << ", message_length: " << s.message_length << " }\n";
   return os;
 }
 
diff --git a/quic/core/frames/quic_message_frame.h b/quic/core/frames/quic_message_frame.h
index 6458a11..4accff2 100644
--- a/quic/core/frames/quic_message_frame.h
+++ b/quic/core/frames/quic_message_frame.h
@@ -6,14 +6,26 @@
 #define QUICHE_QUIC_CORE_FRAMES_QUIC_MESSAGE_FRAME_H_
 
 #include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
 
 namespace quic {
 
+typedef QuicInlinedVector<QuicMemSlice, 1> QuicMessageData;
+
 struct QUIC_EXPORT_PRIVATE QuicMessageFrame {
   QuicMessageFrame();
-  QuicMessageFrame(QuicMessageId message_id, QuicStringPiece message_data);
+  explicit QuicMessageFrame(QuicMessageId message_id);
+  QuicMessageFrame(const char* data, QuicPacketLength length);
+
+  QuicMessageFrame(const QuicMessageFrame& other) = delete;
+  QuicMessageFrame& operator=(const QuicMessageFrame& other) = delete;
+
+  QuicMessageFrame(QuicMessageFrame&& other) = default;
+  QuicMessageFrame& operator=(QuicMessageFrame&& other) = default;
+
   ~QuicMessageFrame();
 
   friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
@@ -23,8 +35,13 @@
   // message_id is only used on the sender side and does not get serialized on
   // wire.
   QuicMessageId message_id;
-  // The actual data.
-  QuicString message_data;
+  // Not owned, only used on read path.
+  const char* data;
+  // Total length of message_data, must be fit into one packet.
+  QuicPacketLength message_length;
+
+  // The actual message data which is reference counted, used on write path.
+  QuicMessageData message_data;
 };
 
 }  // namespace quic
diff --git a/quic/core/frames/quic_retire_connection_id_frame.cc b/quic/core/frames/quic_retire_connection_id_frame.cc
index a507269..6828ce4 100644
--- a/quic/core/frames/quic_retire_connection_id_frame.cc
+++ b/quic/core/frames/quic_retire_connection_id_frame.cc
@@ -1,3 +1,7 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
 #include "net/third_party/quiche/src/quic/core/frames/quic_retire_connection_id_frame.h"
 #include "net/third_party/quiche/src/quic/core/quic_constants.h"
 
diff --git a/quic/core/frames/quic_retire_connection_id_frame.h b/quic/core/frames/quic_retire_connection_id_frame.h
index 7bce51c..79521f6 100644
--- a/quic/core/frames/quic_retire_connection_id_frame.h
+++ b/quic/core/frames/quic_retire_connection_id_frame.h
@@ -1,3 +1,7 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
 #ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_RETIRE_CONNECTION_ID_FRAME_H_
 #define QUICHE_QUIC_CORE_FRAMES_QUIC_RETIRE_CONNECTION_ID_FRAME_H_
 
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index 132da8e..adf82c9 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -10,8 +10,6 @@
 #include <utility>
 #include <vector>
 
-#include "gfe/gfe2/base/epoll_server.h"
-#include "net/util/netutil.h"
 #include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
 #include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
 #include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
@@ -26,6 +24,7 @@
 #include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_port_utils.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_sleep.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
@@ -139,18 +138,21 @@
   ParsedQuicVersionVector all_supported_versions =
       FilterSupportedVersions(AllSupportedVersions());
 
-  // Buckets are separated by the handshake protocol (QUIC crypto or TLS) in
-  // use, since if the handshake protocol changes, the ClientHello/CHLO must be
+  // Buckets are separated by versions: versions prior to QUIC_VERSION_47 use
+  // STREAM frames for the handshake, and only have QUIC crypto as the handshake
+  // protocol. Version 47 and greater use CRYPTO frames for the handshake, and
+  // must also be split based on the handshake protocol. If the handshake
+  // protocol (QUIC crypto or TLS) changes, the ClientHello/CHLO must be
   // reconstructed for the correct protocol.
-  ParsedQuicVersionVector version_buckets[2];
+  ParsedQuicVersionVector version_buckets[3];
 
   for (const ParsedQuicVersion& version : all_supported_versions) {
-    // Versions: 35+
-    // QUIC_VERSION_35 allows endpoints to independently set stream limit.
-    if (version.handshake_protocol == PROTOCOL_TLS1_3) {
+    if (version.transport_version < QUIC_VERSION_47) {
+      version_buckets[0].push_back(version);
+    } else if (version.handshake_protocol == PROTOCOL_QUIC_CRYPTO) {
       version_buckets[1].push_back(version);
     } else {
-      version_buckets[0].push_back(version);
+      version_buckets[2].push_back(version);
     }
   }
 
@@ -273,7 +275,7 @@
       : initialized_(false),
         connect_to_server_on_initialize_(true),
         server_address_(
-            QuicSocketAddress(TestLoopback(), net_util::PickUnusedPortOrDie())),
+            QuicSocketAddress(TestLoopback(), QuicPickUnusedPortOrDie())),
         server_hostname_("test.example.com"),
         client_writer_(nullptr),
         server_writer_(nullptr),
@@ -313,9 +315,7 @@
     AddToCache("/bar", 200, kBarResponseBody);
   }
 
-  ~EndToEndTest() override {
-    net_util::RecycleUnusedPort(server_address_.port());
-  }
+  ~EndToEndTest() override { QuicRecyclePort(server_address_.port()); }
 
   virtual void CreateClientWithWriter() {
     client_.reset(CreateQuicClient(client_writer_));
@@ -404,9 +404,6 @@
       copt.push_back(kTPCC);
     }
 
-    if (support_server_push_) {
-      copt.push_back(kSPSH);
-    }
     if (GetParam().client_supports_stateless_rejects) {
       copt.push_back(kSREJ);
     }
@@ -602,13 +599,15 @@
   }
 
   QuicStreamId GetNthClientInitiatedBidirectionalId(int n) {
-    return QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
-        *client_->client()->client_session(), n);
+    return GetNthClientInitiatedBidirectionalStreamId(
+        client_->client()->client_session()->connection()->transport_version(),
+        n);
   }
 
   QuicStreamId GetNthServerInitiatedBidirectionalId(int n) {
-    return QuicSpdySessionPeer::GetNthServerInitiatedBidirectionalStreamId(
-        *client_->client()->client_session(), n);
+    return GetNthServerInitiatedBidirectionalStreamId(
+        client_->client()->client_session()->connection()->transport_version(),
+        n);
   }
 
   ScopedEnvironmentForThreads environment_;
@@ -639,21 +638,21 @@
 };
 
 // Run all end to end tests with all supported versions.
-INSTANTIATE_TEST_CASE_P(EndToEndTests,
-                        EndToEndTest,
-                        ::testing::ValuesIn(GetTestParams(false, false)));
+INSTANTIATE_TEST_SUITE_P(EndToEndTests,
+                         EndToEndTest,
+                         ::testing::ValuesIn(GetTestParams(false, false)));
 
 class EndToEndTestWithTls : public EndToEndTest {};
 
-INSTANTIATE_TEST_CASE_P(EndToEndTestsWithTls,
-                        EndToEndTestWithTls,
-                        ::testing::ValuesIn(GetTestParams(true, false)));
+INSTANTIATE_TEST_SUITE_P(EndToEndTestsWithTls,
+                         EndToEndTestWithTls,
+                         ::testing::ValuesIn(GetTestParams(true, false)));
 
 class EndToEndTestWithStatelessReject : public EndToEndTest {};
 
-INSTANTIATE_TEST_CASE_P(WithStatelessReject,
-                        EndToEndTestWithStatelessReject,
-                        ::testing::ValuesIn(GetTestParams(false, true)));
+INSTANTIATE_TEST_SUITE_P(WithStatelessReject,
+                         EndToEndTestWithStatelessReject,
+                         ::testing::ValuesIn(GetTestParams(false, true)));
 
 TEST_P(EndToEndTestWithTls, HandshakeSuccessful) {
   ASSERT_TRUE(Initialize());
@@ -709,25 +708,6 @@
             client_->client()->GetNumSentClientHellos());
 }
 
-// TODO(dschinazi) remove this test once the flags are deprecated
-TEST_P(EndToEndTest, SimpleRequestResponseVariableLengthConnectionIDClient) {
-  SetQuicRestartFlag(quic_variable_length_connection_ids_client, true);
-  SetQuicRestartFlag(quic_variable_length_connection_ids_server, false);
-  ASSERT_TRUE(Initialize());
-
-  EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
-  EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
-  int expected_num_client_hellos = 2;
-  if (ServerSendsVersionNegotiation()) {
-    ++expected_num_client_hellos;
-    if (BothSidesSupportStatelessRejects()) {
-      ++expected_num_client_hellos;
-    }
-  }
-  EXPECT_EQ(expected_num_client_hellos,
-            client_->client()->GetNumSentClientHellos());
-}
-
 TEST_P(EndToEndTest, SimpleRequestResponseZeroConnectionID) {
   QuicConnectionId connection_id = QuicUtils::CreateZeroConnectionId(
       GetParam().negotiated_version.transport_version);
@@ -750,25 +730,6 @@
                 GetParam().negotiated_version.transport_version));
 }
 
-// TODO(dschinazi) remove this test once the flags are deprecated
-TEST_P(EndToEndTest, SimpleRequestResponseVariableLengthConnectionIDServer) {
-  SetQuicRestartFlag(quic_variable_length_connection_ids_client, false);
-  SetQuicRestartFlag(quic_variable_length_connection_ids_server, true);
-  ASSERT_TRUE(Initialize());
-
-  EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
-  EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
-  int expected_num_client_hellos = 2;
-  if (ServerSendsVersionNegotiation()) {
-    ++expected_num_client_hellos;
-    if (BothSidesSupportStatelessRejects()) {
-      ++expected_num_client_hellos;
-    }
-  }
-  EXPECT_EQ(expected_num_client_hellos,
-            client_->client()->GetNumSentClientHellos());
-}
-
 TEST_P(EndToEndTest, SimpleRequestResponseWithLargeReject) {
   chlo_multiplier_ = 1;
   ASSERT_TRUE(Initialize());
@@ -1360,7 +1321,7 @@
 
   // Make sure that the stream has data pending so that it will be marked as
   // write blocked when it receives a stream level WINDOW_UPDATE.
-  stream->WriteOrBufferBody("hello", false, nullptr);
+  stream->WriteOrBufferBody("hello", false);
 
   // The stream now attempts to write, fails because it is still connection
   // level flow control blocked, and is added to the write blocked list.
@@ -1949,7 +1910,7 @@
 
   // Open a data stream to make sure the stream level flow control is updated.
   QuicSpdyClientStream* stream = client_->GetOrCreateStream();
-  stream->WriteOrBufferBody("hello", false, nullptr);
+  stream->WriteOrBufferBody("hello", false);
 
   // Client should have the right values for server's receive window.
   EXPECT_EQ(kServerStreamIFCW,
@@ -2005,7 +1966,7 @@
 
   // Open a data stream to make sure the stream level flow control is updated.
   QuicSpdyClientStream* stream = client_->GetOrCreateStream();
-  stream->WriteOrBufferBody("hello", false, nullptr);
+  stream->WriteOrBufferBody("hello", false);
 
   // Client should have the right values for server's receive window.
   EXPECT_EQ(kExpectedStreamIFCW,
@@ -2045,9 +2006,14 @@
 
   QuicCryptoStream* crypto_stream = QuicSessionPeer::GetMutableCryptoStream(
       client_->client()->client_session());
-  EXPECT_LT(
-      QuicFlowControllerPeer::SendWindowSize(crypto_stream->flow_controller()),
-      kStreamIFCW);
+  // In v47 and later, the crypto handshake (sent in CRYPTO frames) is not
+  // subject to flow control.
+  if (client_->client()->client_session()->connection()->transport_version() <
+      QUIC_VERSION_47) {
+    EXPECT_LT(QuicFlowControllerPeer::SendWindowSize(
+                  crypto_stream->flow_controller()),
+              kStreamIFCW);
+  }
   EXPECT_EQ(kSessionIFCW,
             QuicFlowControllerPeer::SendWindowSize(
                 client_->client()->client_session()->flow_controller()));
@@ -2223,20 +2189,9 @@
   QuicString request_string =
       "a request body bigger than one packet" + QuicString(kMaxPacketSize, '.');
 
-  // Calculate header length for version 99, so that the ack listener know how
-  // many actual bytes will be acked.
-  QuicByteCount header_length = 0;
-  if (client_->client()->client_session()->connection()->transport_version() ==
-      QUIC_VERSION_99) {
-    HttpEncoder encoder;
-    std::unique_ptr<char[]> buf;
-    header_length =
-        encoder.SerializeDataFrameHeader(request_string.length(), &buf);
-  }
-
   // The TestAckListener will cause a failure if not notified.
   QuicReferenceCountedPointer<TestAckListener> ack_listener(
-      new TestAckListener(request_string.length() + header_length));
+      new TestAckListener(request_string.length()));
 
   // Send the request, and register the delegate for ACKs.
   client_->SendData(request_string, true, ack_listener);
@@ -2875,9 +2830,9 @@
 };
 
 // Run all server push end to end tests with all supported versions.
-INSTANTIATE_TEST_CASE_P(EndToEndTestsServerPush,
-                        EndToEndTestServerPush,
-                        ::testing::ValuesIn(GetTestParams(false, false)));
+INSTANTIATE_TEST_SUITE_P(EndToEndTestsServerPush,
+                         EndToEndTestServerPush,
+                         ::testing::ValuesIn(GetTestParams(false, false)));
 
 TEST_P(EndToEndTestServerPush, ServerPush) {
   ASSERT_TRUE(Initialize());
@@ -3266,7 +3221,6 @@
   QuicConnection* client_connection =
       client_->client()->client_session()->connection();
   client_connection->set_debug_visitor(&observer);
-  QuicTransportVersion version = client_connection->transport_version();
   // 100KB body.
   QuicString body(100 * 1024, 'a');
   SpdyHeaderBlock headers;
@@ -3278,12 +3232,8 @@
   EXPECT_EQ(kFooResponseBody,
             client_->SendCustomSynchronousRequest(headers, body));
   client_->Disconnect();
-  if (version != QUIC_VERSION_35) {
-    EXPECT_LT(0u, observer.num_window_update_frames());
-    EXPECT_EQ(0u, observer.num_ping_frames());
-  } else {
-    EXPECT_EQ(0u, observer.num_window_update_frames());
-  }
+  EXPECT_LT(0u, observer.num_window_update_frames());
+  EXPECT_EQ(0u, observer.num_ping_frames());
 }
 
 TEST_P(EndToEndTest, SendStatelessResetTokenInShlo) {
@@ -3506,13 +3456,12 @@
 
   // 1 MB body.
   QuicString body(1024 * 1024, 'a');
-  stream->WriteOrBufferBody(body, true, nullptr);
+  stream->WriteOrBufferBody(body, true);
   client_->WaitForResponse();
   EXPECT_EQ(QUIC_STREAM_TTL_EXPIRED, client_->stream_error());
 }
 
 TEST_P(EndToEndTest, SendMessages) {
-  SetQuicReloadableFlag(quic_fix_mark_for_loss_retransmission, true);
   ASSERT_TRUE(Initialize());
   EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
   QuicSession* client_session = client_->client()->client_session();
@@ -3529,22 +3478,30 @@
   QuicStringPiece message_buffer(message_string);
   QuicRandom* random =
       QuicConnectionPeer::GetHelper(client_connection)->GetRandomGenerator();
+  QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
   {
     QuicConnection::ScopedPacketFlusher flusher(
         client_session->connection(), QuicConnection::SEND_ACK_IF_PENDING);
     // Verify the largest message gets successfully sent.
     EXPECT_EQ(MessageResult(MESSAGE_STATUS_SUCCESS, 1),
-              client_session->SendMessage(
+              client_session->SendMessage(MakeSpan(
+                  client_session->connection()
+                      ->helper()
+                      ->GetStreamSendBufferAllocator(),
                   QuicStringPiece(message_buffer.data(),
-                                  client_session->GetLargestMessagePayload())));
+                                  client_session->GetLargestMessagePayload()),
+                  &storage)));
     // Send more messages with size (0, largest_payload] until connection is
     // write blocked.
     const int kTestMaxNumberOfMessages = 100;
     for (size_t i = 2; i <= kTestMaxNumberOfMessages; ++i) {
       size_t message_length =
           random->RandUint64() % client_session->GetLargestMessagePayload() + 1;
-      MessageResult result = client_session->SendMessage(
-          QuicStringPiece(message_buffer.data(), message_length));
+      MessageResult result = client_session->SendMessage(MakeSpan(
+          client_session->connection()
+              ->helper()
+              ->GetStreamSendBufferAllocator(),
+          QuicStringPiece(message_buffer.data(), message_length), &storage));
       if (result.status == MESSAGE_STATUS_BLOCKED) {
         // Connection is write blocked.
         break;
@@ -3554,12 +3511,17 @@
   }
 
   client_->WaitForDelayedAcks();
-  EXPECT_EQ(MESSAGE_STATUS_TOO_LARGE,
-            client_session
-                ->SendMessage(QuicStringPiece(
-                    message_buffer.data(),
-                    client_session->GetLargestMessagePayload() + 1))
-                .status);
+  EXPECT_EQ(
+      MESSAGE_STATUS_TOO_LARGE,
+      client_session
+          ->SendMessage(MakeSpan(
+              client_session->connection()
+                  ->helper()
+                  ->GetStreamSendBufferAllocator(),
+              QuicStringPiece(message_buffer.data(),
+                              client_session->GetLargestMessagePayload() + 1),
+              &storage))
+          .status);
   EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error());
 }
 
@@ -3580,9 +3542,9 @@
   PacketReorderingWriter* reorder_writer_;
 };
 
-INSTANTIATE_TEST_CASE_P(EndToEndPacketReorderingTests,
-                        EndToEndPacketReorderingTest,
-                        testing::ValuesIn(GetTestParams(false, false)));
+INSTANTIATE_TEST_SUITE_P(EndToEndPacketReorderingTests,
+                         EndToEndPacketReorderingTest,
+                         testing::ValuesIn(GetTestParams(false, false)));
 
 TEST_P(EndToEndPacketReorderingTest, ReorderedConnectivityProbing) {
   ASSERT_TRUE(Initialize());
diff --git a/quic/core/http/http_decoder.cc b/quic/core/http/http_decoder.cc
index 345f2cb..e697821 100644
--- a/quic/core/http/http_decoder.cc
+++ b/quic/core/http/http_decoder.cc
@@ -22,7 +22,7 @@
 }
 
 // Length of the type field of HTTP/3 frames.
-static const size_t kFrameTypeLength = 1;
+static const QuicByteCount kFrameTypeLength = 1;
 
 }  // namespace
 
@@ -40,9 +40,9 @@
 
 HttpDecoder::~HttpDecoder() {}
 
-size_t HttpDecoder::ProcessInput(const char* data, size_t len) {
+QuicByteCount HttpDecoder::ProcessInput(const char* data, QuicByteCount len) {
   has_payload_ = false;
-  QuicDataReader reader(data, len, NETWORK_BYTE_ORDER);
+  QuicDataReader reader(data, len);
   while (error_ == QUIC_NO_ERROR && reader.BytesRemaining() != 0) {
     switch (state_) {
       case STATE_READING_FRAME_LENGTH:
@@ -75,7 +75,7 @@
     return;
   }
   QuicDataReader length_reader(length_buffer_.data(),
-                               current_length_field_size_, NETWORK_BYTE_ORDER);
+                               current_length_field_size_);
   if (!length_reader.ReadVarInt62(&current_frame_length_)) {
     RaiseError(QUIC_INTERNAL_ERROR, "Unable to read frame length");
     visitor_->OnError(this);
@@ -105,8 +105,8 @@
             Http3FrameLengths(current_length_field_size_ + kFrameTypeLength,
                               current_frame_length_));
       }
-      size_t bytes_to_read =
-          std::min<size_t>(remaining_frame_length_, reader->BytesRemaining());
+      QuicByteCount bytes_to_read = std::min<QuicByteCount>(
+          remaining_frame_length_, reader->BytesRemaining());
       QuicStringPiece payload;
       if (!reader->ReadStringPiece(&payload, bytes_to_read)) {
         RaiseError(QUIC_INTERNAL_ERROR, "Unable to read data");
@@ -126,8 +126,8 @@
       if (current_frame_length_ == remaining_frame_length_) {
         visitor_->OnHeadersFrameStart();
       }
-      size_t bytes_to_read =
-          std::min<size_t>(remaining_frame_length_, reader->BytesRemaining());
+      QuicByteCount bytes_to_read = std::min<QuicByteCount>(
+          remaining_frame_length_, reader->BytesRemaining());
       QuicStringPiece payload;
       if (!reader->ReadStringPiece(&payload, bytes_to_read)) {
         RaiseError(QUIC_INTERNAL_ERROR, "Unable to read data");
@@ -138,7 +138,7 @@
       if (remaining_frame_length_ == 0) {
         state_ = STATE_READING_FRAME_LENGTH;
         current_length_field_size_ = 0;
-        visitor_->OnHeadersFrameEnd();
+        visitor_->OnHeadersFrameEnd(current_frame_length_);
       }
       return;
     }
@@ -148,8 +148,7 @@
       BufferFramePayload(reader);
       if (remaining_frame_length_ == 0) {
         PriorityFrame frame;
-        QuicDataReader reader(buffer_.data(), current_frame_length_,
-                              NETWORK_BYTE_ORDER);
+        QuicDataReader reader(buffer_.data(), current_frame_length_);
         if (!ParsePriorityFrame(&reader, &frame)) {
           return;
         }
@@ -164,8 +163,7 @@
       BufferFramePayload(reader);
       if (remaining_frame_length_ == 0) {
         CancelPushFrame frame;
-        QuicDataReader reader(buffer_.data(), current_frame_length_,
-                              NETWORK_BYTE_ORDER);
+        QuicDataReader reader(buffer_.data(), current_frame_length_);
         if (!reader.ReadVarInt62(&frame.push_id)) {
           RaiseError(QUIC_INTERNAL_ERROR, "Unable to read push_id");
           return;
@@ -184,8 +182,7 @@
       BufferFramePayload(reader);
       if (remaining_frame_length_ == 0) {
         SettingsFrame frame;
-        QuicDataReader reader(buffer_.data(), current_frame_length_,
-                              NETWORK_BYTE_ORDER);
+        QuicDataReader reader(buffer_.data(), current_frame_length_);
         if (!ParseSettingsFrame(&reader, &frame)) {
           return;
         }
@@ -197,7 +194,7 @@
     }
     case 0x5: {  // PUSH_PROMISE
       if (current_frame_length_ == remaining_frame_length_) {
-        size_t bytes_remaining = reader->BytesRemaining();
+        QuicByteCount bytes_remaining = reader->BytesRemaining();
         PushId push_id;
         // TODO(rch): Handle partial delivery of this field.
         if (!reader->ReadVarInt62(&push_id)) {
@@ -207,8 +204,8 @@
         remaining_frame_length_ -= bytes_remaining - reader->BytesRemaining();
         visitor_->OnPushPromiseFrameStart(push_id);
       }
-      size_t bytes_to_read =
-          std::min<size_t>(remaining_frame_length_, reader->BytesRemaining());
+      QuicByteCount bytes_to_read = std::min<QuicByteCount>(
+          remaining_frame_length_, reader->BytesRemaining());
       if (bytes_to_read == 0) {
         return;
       }
@@ -230,8 +227,7 @@
       BufferFramePayload(reader);
       if (remaining_frame_length_ == 0) {
         GoAwayFrame frame;
-        QuicDataReader reader(buffer_.data(), current_frame_length_,
-                              NETWORK_BYTE_ORDER);
+        QuicDataReader reader(buffer_.data(), current_frame_length_);
         uint64_t stream_id;
         if (!reader.ReadVarInt62(&stream_id)) {
           RaiseError(QUIC_INTERNAL_ERROR, "Unable to read GOAWAY stream_id");
@@ -249,8 +245,7 @@
       // TODO(rch): Handle partial delivery.
       BufferFramePayload(reader);
       if (remaining_frame_length_ == 0) {
-        QuicDataReader reader(buffer_.data(), current_frame_length_,
-                              NETWORK_BYTE_ORDER);
+        QuicDataReader reader(buffer_.data(), current_frame_length_);
         MaxPushIdFrame frame;
         if (!reader.ReadVarInt62(&frame.push_id)) {
           RaiseError(QUIC_INTERNAL_ERROR, "Unable to read push_id");
@@ -268,8 +263,7 @@
       if (remaining_frame_length_ != 0) {
         return;
       }
-      QuicDataReader reader(buffer_.data(), current_frame_length_,
-                            NETWORK_BYTE_ORDER);
+      QuicDataReader reader(buffer_.data(), current_frame_length_);
       DuplicatePushFrame frame;
       if (!reader.ReadVarInt62(&frame.push_id)) {
         RaiseError(QUIC_INTERNAL_ERROR, "Unable to read push_id");
@@ -305,8 +299,8 @@
 }
 
 void HttpDecoder::DiscardFramePayload(QuicDataReader* reader) {
-  size_t bytes_to_read =
-      std::min<size_t>(remaining_frame_length_, reader->BytesRemaining());
+  QuicByteCount bytes_to_read = std::min<QuicByteCount>(
+      remaining_frame_length_, reader->BytesRemaining());
   QuicStringPiece payload;
   if (!reader->ReadStringPiece(&payload, bytes_to_read)) {
     RaiseError(QUIC_INTERNAL_ERROR, "Unable to read frame payload");
@@ -324,8 +318,8 @@
     buffer_.erase(buffer_.size());
     buffer_.reserve(current_frame_length_);
   }
-  size_t bytes_to_read =
-      std::min<size_t>(remaining_frame_length_, reader->BytesRemaining());
+  QuicByteCount bytes_to_read = std::min<QuicByteCount>(
+      remaining_frame_length_, reader->BytesRemaining());
   if (!reader->ReadBytes(
           &(buffer_[0]) + current_frame_length_ - remaining_frame_length_,
           bytes_to_read)) {
@@ -349,8 +343,8 @@
     length_buffer_.erase(length_buffer_.size());
     length_buffer_.reserve(current_length_field_size_);
   }
-  size_t bytes_to_read = std::min<size_t>(remaining_length_field_length_,
-                                          reader->BytesRemaining());
+  QuicByteCount bytes_to_read = std::min<QuicByteCount>(
+      remaining_length_field_length_, reader->BytesRemaining());
   if (!reader->ReadBytes(&(length_buffer_[0]) + current_length_field_size_ -
                              remaining_length_field_length_,
                          bytes_to_read)) {
diff --git a/quic/core/http/http_decoder.h b/quic/core/http/http_decoder.h
index a5f41b5..910d189 100644
--- a/quic/core/http/http_decoder.h
+++ b/quic/core/http/http_decoder.h
@@ -9,6 +9,7 @@
 
 #include "net/third_party/quiche/src/quic/core/http/http_frames.h"
 #include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
 
@@ -20,7 +21,7 @@
 // |header_length| stores number of bytes header occupies.
 // |payload_length| stores number of bytes payload occupies.
 struct QUIC_EXPORT_PRIVATE Http3FrameLengths {
-  Http3FrameLengths(uint64_t header, uint64_t payload)
+  Http3FrameLengths(QuicByteCount header, QuicByteCount payload)
       : header_length(header), payload_length(payload) {}
 
   bool operator==(const Http3FrameLengths& other) const {
@@ -76,7 +77,8 @@
     // multiple times for a single frame.
     virtual void OnHeadersFramePayload(QuicStringPiece payload) = 0;
     // Called when a HEADERS frame has been completely processed.
-    virtual void OnHeadersFrameEnd() = 0;
+    // |frame_len| is the length of the HEADERS frame payload.
+    virtual void OnHeadersFrameEnd(QuicByteCount frame_len) = 0;
 
     // Called when a PUSH_PROMISE frame has been recevied for |push_id|.
     virtual void OnPushPromiseFrameStart(PushId push_id) = 0;
@@ -104,7 +106,7 @@
   // Processes the input and invokes the visitor for any frames.
   // Returns the number of bytes consumed, or 0 if there was an error, in which
   // case error() should be consulted.
-  size_t ProcessInput(const char* data, size_t len);
+  QuicByteCount ProcessInput(const char* data, QuicByteCount len);
 
   bool has_payload() { return has_payload_; }
 
@@ -158,13 +160,13 @@
   // Type of the frame currently being parsed.
   uint8_t current_frame_type_;
   // Size of the frame's length field.
-  uint64_t current_length_field_size_;
+  QuicByteCount current_length_field_size_;
   // Remaining length that's needed for the frame's length field.
-  uint64_t remaining_length_field_length_;
+  QuicByteCount remaining_length_field_length_;
   // Length of the payload of the frame currently being parsed.
-  uint64_t current_frame_length_;
+  QuicByteCount current_frame_length_;
   // Remaining payload bytes to be parsed.
-  uint64_t remaining_frame_length_;
+  QuicByteCount remaining_frame_length_;
   // Last error.
   QuicErrorCode error_;
   // The issue which caused |error_|
diff --git a/quic/core/http/http_decoder_test.cc b/quic/core/http/http_decoder_test.cc
index c2b303c..c6d7d9f 100644
--- a/quic/core/http/http_decoder_test.cc
+++ b/quic/core/http/http_decoder_test.cc
@@ -31,7 +31,7 @@
 
   MOCK_METHOD0(OnHeadersFrameStart, void());
   MOCK_METHOD1(OnHeadersFramePayload, void(QuicStringPiece payload));
-  MOCK_METHOD0(OnHeadersFrameEnd, void());
+  MOCK_METHOD1(OnHeadersFrameEnd, void(QuicByteCount frame_len));
 
   MOCK_METHOD1(OnPushPromiseFrameStart, void(PushId push_id));
   MOCK_METHOD1(OnPushPromiseFramePayload, void(QuicStringPiece payload));
@@ -84,7 +84,7 @@
 TEST_F(HttpDecoderTest, ReservedFramesLargePayload) {
   for (int n = 0; n < 8; ++n) {
     const uint8_t type = 0xB + 0x1F * n;
-    const size_t payload_size = 256;
+    const QuicByteCount payload_size = 256;
     char input[payload_size + 3] = {// length
                                     0x40 + 0x01, 0x00,
                                     // type
@@ -383,7 +383,7 @@
   InSequence s;
   EXPECT_CALL(visitor_, OnHeadersFrameStart());
   EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("Headers")));
-  EXPECT_CALL(visitor_, OnHeadersFrameEnd());
+  EXPECT_CALL(visitor_, OnHeadersFrameEnd(7));
   EXPECT_EQ(QUIC_ARRAYSIZE(input),
             decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input)));
   EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
@@ -398,7 +398,7 @@
   EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("e")));
   EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("r")));
   EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("s")));
-  EXPECT_CALL(visitor_, OnHeadersFrameEnd());
+  EXPECT_CALL(visitor_, OnHeadersFrameEnd(7));
   for (char c : input) {
     EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1));
   }
diff --git a/quic/core/http/http_encoder.cc b/quic/core/http/http_encoder.cc
index 7c07838..d2d1699 100644
--- a/quic/core/http/http_encoder.cc
+++ b/quic/core/http/http_encoder.cc
@@ -66,7 +66,7 @@
       QuicDataWriter::GetVarInt62Len(payload_length) + kFrameTypeLength;
 
   output->reset(new char[header_length]);
-  QuicDataWriter writer(header_length, output->get(), NETWORK_BYTE_ORDER);
+  QuicDataWriter writer(header_length, output->get());
 
   if (WriteFrameHeader(payload_length, HttpFrameType::DATA, &writer)) {
     return header_length;
@@ -82,7 +82,7 @@
       QuicDataWriter::GetVarInt62Len(payload_length) + kFrameTypeLength;
 
   output->reset(new char[header_length]);
-  QuicDataWriter writer(header_length, output->get(), NETWORK_BYTE_ORDER);
+  QuicDataWriter writer(header_length, output->get());
 
   if (WriteFrameHeader(payload_length, HttpFrameType::HEADERS, &writer)) {
     return header_length;
@@ -101,7 +101,7 @@
   QuicByteCount total_length = GetTotalLength(payload_length);
 
   output->reset(new char[total_length]);
-  QuicDataWriter writer(total_length, output->get(), NETWORK_BYTE_ORDER);
+  QuicDataWriter writer(total_length, output->get());
 
   if (!WriteFrameHeader(payload_length, HttpFrameType::PRIORITY, &writer)) {
     return 0;
@@ -132,7 +132,7 @@
   QuicByteCount total_length = GetTotalLength(payload_length);
 
   output->reset(new char[total_length]);
-  QuicDataWriter writer(total_length, output->get(), NETWORK_BYTE_ORDER);
+  QuicDataWriter writer(total_length, output->get());
 
   if (WriteFrameHeader(payload_length, HttpFrameType::CANCEL_PUSH, &writer) &&
       writer.WriteVarInt62(cancel_push.push_id)) {
@@ -154,7 +154,7 @@
   QuicByteCount total_length = GetTotalLength(payload_length);
 
   output->reset(new char[total_length]);
-  QuicDataWriter writer(total_length, output->get(), NETWORK_BYTE_ORDER);
+  QuicDataWriter writer(total_length, output->get());
 
   if (!WriteFrameHeader(payload_length, HttpFrameType::SETTINGS, &writer)) {
     return 0;
@@ -181,7 +181,7 @@
       QuicDataWriter::GetVarInt62Len(push_promise.push_id);
 
   output->reset(new char[total_length]);
-  QuicDataWriter writer(total_length, output->get(), NETWORK_BYTE_ORDER);
+  QuicDataWriter writer(total_length, output->get());
 
   if (WriteFrameHeader(payload_length, HttpFrameType::PUSH_PROMISE, &writer) &&
       writer.WriteVarInt62(push_promise.push_id)) {
@@ -198,7 +198,7 @@
   QuicByteCount total_length = GetTotalLength(payload_length);
 
   output->reset(new char[total_length]);
-  QuicDataWriter writer(total_length, output->get(), NETWORK_BYTE_ORDER);
+  QuicDataWriter writer(total_length, output->get());
 
   if (WriteFrameHeader(payload_length, HttpFrameType::GOAWAY, &writer) &&
       writer.WriteVarInt62(goaway.stream_id)) {
@@ -215,7 +215,7 @@
   QuicByteCount total_length = GetTotalLength(payload_length);
 
   output->reset(new char[total_length]);
-  QuicDataWriter writer(total_length, output->get(), NETWORK_BYTE_ORDER);
+  QuicDataWriter writer(total_length, output->get());
 
   if (WriteFrameHeader(payload_length, HttpFrameType::MAX_PUSH_ID, &writer) &&
       writer.WriteVarInt62(max_push_id.push_id)) {
@@ -232,7 +232,7 @@
   QuicByteCount total_length = GetTotalLength(payload_length);
 
   output->reset(new char[total_length]);
-  QuicDataWriter writer(total_length, output->get(), NETWORK_BYTE_ORDER);
+  QuicDataWriter writer(total_length, output->get());
 
   if (WriteFrameHeader(payload_length, HttpFrameType::DUPLICATE_PUSH,
                        &writer) &&
diff --git a/quic/core/http/quic_client_promised_info_test.cc b/quic/core/http/quic_client_promised_info_test.cc
index 2cf3f76..2a7b1b0 100644
--- a/quic/core/http/quic_client_promised_info_test.cc
+++ b/quic/core/http/quic_client_promised_info_test.cc
@@ -84,8 +84,8 @@
     headers_["content-length"] = "11";
 
     stream_ = QuicMakeUnique<QuicSpdyClientStream>(
-        QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
-            session_, 0),
+        GetNthClientInitiatedBidirectionalStreamId(
+            connection_->transport_version(), 0),
         &session_, BIDIRECTIONAL);
     stream_visitor_ = QuicMakeUnique<StreamVisitor>();
     stream_->set_visitor(stream_visitor_.get());
@@ -99,9 +99,8 @@
     promise_url_ = SpdyUtils::GetPromisedUrlFromHeaders(push_promise_);
 
     client_request_ = push_promise_.Clone();
-    promise_id_ =
-        QuicSpdySessionPeer::GetNthServerInitiatedUnidirectionalStreamId(
-            session_, 0);
+    promise_id_ = GetNthServerInitiatedUnidirectionalStreamId(
+        connection_->transport_version(), 0);
   }
 
   class StreamVisitor : public QuicSpdyClientStream::Visitor {
diff --git a/quic/core/http/quic_client_push_promise_index_test.cc b/quic/core/http/quic_client_push_promise_index_test.cc
index 0ddde61..b68b41f 100644
--- a/quic/core/http/quic_client_push_promise_index_test.cc
+++ b/quic/core/http/quic_client_push_promise_index_test.cc
@@ -54,12 +54,11 @@
                                                        &alarm_factory_,
                                                        Perspective::IS_CLIENT)),
         session_(connection_->supported_versions(), connection_, &index_),
-        promised_(
-            &session_,
-            QuicSpdySessionPeer::GetNthServerInitiatedUnidirectionalStreamId(
-                session_,
-                0),
-            url_) {
+        promised_(&session_,
+                  GetNthServerInitiatedUnidirectionalStreamId(
+                      connection_->transport_version(),
+                      0),
+                  url_) {
     request_[":path"] = "/bar";
     request_[":authority"] = "www.google.com";
     request_[":version"] = "HTTP/1.1";
diff --git a/quic/core/http/quic_headers_stream.cc b/quic/core/http/quic_headers_stream.cc
index 09d8797..9c99ab7 100644
--- a/quic/core/http/quic_headers_stream.cc
+++ b/quic/core/http/quic_headers_stream.cc
@@ -60,7 +60,8 @@
 bool QuicHeadersStream::OnStreamFrameAcked(QuicStreamOffset offset,
                                            QuicByteCount data_length,
                                            bool fin_acked,
-                                           QuicTime::Delta ack_delay_time) {
+                                           QuicTime::Delta ack_delay_time,
+                                           QuicByteCount* newly_acked_length) {
   QuicIntervalSet<QuicStreamOffset> newly_acked(offset, offset + data_length);
   newly_acked.Difference(bytes_acked());
   for (const auto& acked : newly_acked) {
@@ -104,7 +105,7 @@
     unacked_headers_.pop_front();
   }
   return QuicStream::OnStreamFrameAcked(offset, data_length, fin_acked,
-                                        ack_delay_time);
+                                        ack_delay_time, newly_acked_length);
 }
 
 void QuicHeadersStream::OnStreamFrameRetransmitted(QuicStreamOffset offset,
diff --git a/quic/core/http/quic_headers_stream.h b/quic/core/http/quic_headers_stream.h
index 4abfb01..abc0aeb 100644
--- a/quic/core/http/quic_headers_stream.h
+++ b/quic/core/http/quic_headers_stream.h
@@ -43,7 +43,8 @@
   bool OnStreamFrameAcked(QuicStreamOffset offset,
                           QuicByteCount data_length,
                           bool fin_acked,
-                          QuicTime::Delta ack_delay_time) override;
+                          QuicTime::Delta ack_delay_time,
+                          QuicByteCount* newly_acked_length) override;
 
   void OnStreamFrameRetransmitted(QuicStreamOffset offset,
                                   QuicByteCount data_length,
diff --git a/quic/core/http/quic_headers_stream_test.cc b/quic/core/http/quic_headers_stream_test.cc
index 1bbb9e4..b6409b1 100644
--- a/quic/core/http/quic_headers_stream_test.cc
+++ b/quic/core/http/quic_headers_stream_test.cc
@@ -195,21 +195,19 @@
     EXPECT_EQ(transport_version(), session_.connection()->transport_version());
     EXPECT_TRUE(headers_stream_ != nullptr);
     connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
-    client_id_1_ =
-        QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
-            session_, 0);
-    client_id_2_ =
-        QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
-            session_, 1);
-    client_id_3_ =
-        QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
-            session_, 2);
-    next_stream_id_ = QuicSpdySessionPeer::StreamIdDelta(session_);
+    client_id_1_ = GetNthClientInitiatedBidirectionalStreamId(
+        connection_->transport_version(), 0);
+    client_id_2_ = GetNthClientInitiatedBidirectionalStreamId(
+        connection_->transport_version(), 1);
+    client_id_3_ = GetNthClientInitiatedBidirectionalStreamId(
+        connection_->transport_version(), 2);
+    next_stream_id_ =
+        QuicUtils::StreamIdDelta(connection_->transport_version());
   }
 
   QuicStreamId GetNthClientInitiatedId(int n) {
-    return QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
-        session_, n);
+    return GetNthClientInitiatedBidirectionalStreamId(
+        connection_->transport_version(), n);
   }
 
   QuicConsumedData SaveIov(size_t write_length) {
@@ -278,9 +276,8 @@
                                          connection_->transport_version()),
                                      _, _, NO_FIN))
         .WillOnce(WithArgs<2>(Invoke(this, &QuicHeadersStreamTest::SaveIov)));
-    QuicSpdySessionPeer::WriteHeadersImpl(
-        &session_, stream_id, headers_.Clone(), fin,
-        Spdy3PriorityToHttp2Weight(priority), 0, false, nullptr);
+    QuicSpdySessionPeer::WriteHeadersOnHeadersStream(
+        &session_, stream_id, headers_.Clone(), fin, priority, nullptr);
 
     // Parse the outgoing data and check that it matches was was written.
     if (is_request) {
@@ -363,9 +360,8 @@
 };
 
 // Run all tests with each version and perspective (client or server).
-INSTANTIATE_TEST_CASE_P(Tests,
-                        QuicHeadersStreamTest,
-                        ::testing::ValuesIn(GetTestParams()));
+INSTANTIATE_TEST_SUITE_P(Tests, QuicHeadersStreamTest,
+                         ::testing::ValuesIn(GetTestParams()));
 
 TEST_P(QuicHeadersStreamTest, StreamId) {
   EXPECT_EQ(QuicUtils::GetHeadersStreamId(connection_->transport_version()),
@@ -833,27 +829,34 @@
   headers_stream_->OnStreamFrameRetransmitted(28, 7, false);
 
   // Packets are acked in order: 2, 3, 1.
+  QuicByteCount newly_acked_length = 0;
   EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _));
   EXPECT_CALL(*ack_listener2, OnPacketAcked(7, _));
-  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(21, 7, false,
-                                                  QuicTime::Delta::Zero()));
-  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(28, 7, false,
-                                                  QuicTime::Delta::Zero()));
+  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+      21, 7, false, QuicTime::Delta::Zero(), &newly_acked_length));
+  EXPECT_EQ(7u, newly_acked_length);
+  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+      28, 7, false, QuicTime::Delta::Zero(), &newly_acked_length));
+  EXPECT_EQ(7u, newly_acked_length);
 
   EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _));
-  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(35, 7, false,
-                                                  QuicTime::Delta::Zero()));
+  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+      35, 7, false, QuicTime::Delta::Zero(), &newly_acked_length));
+  EXPECT_EQ(7u, newly_acked_length);
 
   EXPECT_CALL(*ack_listener1, OnPacketAcked(7, _));
   EXPECT_CALL(*ack_listener1, OnPacketAcked(7, _));
-  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(0, 7, false,
-                                                  QuicTime::Delta::Zero()));
-  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(7, 7, false,
-                                                  QuicTime::Delta::Zero()));
+  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+      0, 7, false, QuicTime::Delta::Zero(), &newly_acked_length));
+  EXPECT_EQ(7u, newly_acked_length);
+  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+      7, 7, false, QuicTime::Delta::Zero(), &newly_acked_length));
+  EXPECT_EQ(7u, newly_acked_length);
   // Unsent data is acked.
   EXPECT_CALL(*ack_listener2, OnPacketAcked(7, _));
-  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(14, 10, false,
-                                                  QuicTime::Delta::Zero()));
+  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+      14, 10, false, QuicTime::Delta::Zero(), &newly_acked_length));
+  EXPECT_EQ(7u, newly_acked_length);
 }
 
 TEST_P(QuicHeadersStreamTest, FrameContainsMultipleHeaders) {
@@ -884,21 +887,25 @@
   headers_stream_->OnStreamFrameRetransmitted(0, 17, false);
 
   // Frames are acked in order: 2, 3, 1.
+  QuicByteCount newly_acked_length = 0;
   EXPECT_CALL(*ack_listener2, OnPacketAcked(4, _));
   EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _));
   EXPECT_CALL(*ack_listener2, OnPacketAcked(2, _));
-  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(17, 13, false,
-                                                  QuicTime::Delta::Zero()));
+  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+      17, 13, false, QuicTime::Delta::Zero(), &newly_acked_length));
+  EXPECT_EQ(13u, newly_acked_length);
 
   EXPECT_CALL(*ack_listener2, OnPacketAcked(5, _));
   EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _));
-  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(30, 12, false,
-                                                  QuicTime::Delta::Zero()));
+  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+      30, 12, false, QuicTime::Delta::Zero(), &newly_acked_length));
+  EXPECT_EQ(12u, newly_acked_length);
 
   EXPECT_CALL(*ack_listener1, OnPacketAcked(14, _));
   EXPECT_CALL(*ack_listener2, OnPacketAcked(3, _));
-  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(0, 17, false,
-                                                  QuicTime::Delta::Zero()));
+  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+      0, 17, false, QuicTime::Delta::Zero(), &newly_acked_length));
+  EXPECT_EQ(17u, newly_acked_length);
 }
 
 TEST_P(QuicHeadersStreamTest, HeadersGetAckedMultipleTimes) {
@@ -924,30 +931,36 @@
   headers_stream_->WriteOrBufferData("Header9", false, ack_listener3);
 
   // Ack [15, 20), [5, 25), [10, 17), [0, 12) and [22, 42).
+  QuicByteCount newly_acked_length = 0;
   EXPECT_CALL(*ack_listener2, OnPacketAcked(5, _));
-  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(15, 5, false,
-                                                  QuicTime::Delta::Zero()));
+  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+      15, 5, false, QuicTime::Delta::Zero(), &newly_acked_length));
+  EXPECT_EQ(5u, newly_acked_length);
 
   EXPECT_CALL(*ack_listener1, OnPacketAcked(9, _));
   EXPECT_CALL(*ack_listener2, OnPacketAcked(1, _));
   EXPECT_CALL(*ack_listener2, OnPacketAcked(1, _));
   EXPECT_CALL(*ack_listener3, OnPacketAcked(4, _));
-  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(5, 20, false,
-                                                  QuicTime::Delta::Zero()));
+  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+      5, 20, false, QuicTime::Delta::Zero(), &newly_acked_length));
+  EXPECT_EQ(15u, newly_acked_length);
 
   // Duplicate ack.
-  EXPECT_FALSE(headers_stream_->OnStreamFrameAcked(10, 7, false,
-                                                   QuicTime::Delta::Zero()));
+  EXPECT_FALSE(headers_stream_->OnStreamFrameAcked(
+      10, 7, false, QuicTime::Delta::Zero(), &newly_acked_length));
+  EXPECT_EQ(0u, newly_acked_length);
 
   EXPECT_CALL(*ack_listener1, OnPacketAcked(5, _));
-  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(0, 12, false,
-                                                  QuicTime::Delta::Zero()));
+  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+      0, 12, false, QuicTime::Delta::Zero(), &newly_acked_length));
+  EXPECT_EQ(5u, newly_acked_length);
 
   EXPECT_CALL(*ack_listener3, OnPacketAcked(3, _));
   EXPECT_CALL(*ack_listener2, OnPacketAcked(7, _));
   EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _));
-  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(22, 20, false,
-                                                  QuicTime::Delta::Zero()));
+  EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+      22, 20, false, QuicTime::Delta::Zero(), &newly_acked_length));
+  EXPECT_EQ(17u, newly_acked_length);
 }
 
 }  // namespace
diff --git a/quic/core/http/quic_server_session_base.cc b/quic/core/http/quic_server_session_base.cc
index 40b43dc..b18b1fa 100644
--- a/quic/core/http/quic_server_session_base.cc
+++ b/quic/core/http/quic_server_session_base.cc
@@ -55,11 +55,6 @@
   bandwidth_resumption_enabled_ =
       last_bandwidth_resumption || max_bandwidth_resumption;
 
-  if (connection()->transport_version() < QUIC_VERSION_35) {
-    set_server_push_enabled(
-        ContainsQuicTag(config()->ReceivedConnectionOptions(), kSPSH));
-  }
-
   // If the client has provided a bandwidth estimate from the same serving
   // region as this server, then decide whether to use the data for bandwidth
   // resumption.
diff --git a/quic/core/http/quic_server_session_base_test.cc b/quic/core/http/quic_server_session_base_test.cc
index 9f36e37..2b75f70 100644
--- a/quic/core/http/quic_server_session_base_test.cc
+++ b/quic/core/http/quic_server_session_base_test.cc
@@ -65,7 +65,7 @@
                               compressed_certs_cache),
         quic_simple_server_backend_(quic_simple_server_backend) {}
 
-  ~TestServerSession() override { delete connection(); };
+  ~TestServerSession() override { delete connection(); }
 
  protected:
   QuicSpdyStream* CreateIncomingStream(QuicStreamId id) override {
@@ -157,13 +157,13 @@
   }
 
   QuicStreamId GetNthClientInitiatedBidirectionalId(int n) {
-    return QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
-        *session_, n);
+    return GetNthClientInitiatedBidirectionalStreamId(
+        connection_->transport_version(), n);
   }
 
   QuicStreamId GetNthServerInitiatedUnidirectionalId(int n) {
-    return QuicSpdySessionPeer::GetNthServerInitiatedUnidirectionalStreamId(
-        *session_, n);
+    return quic::test::GetNthServerInitiatedUnidirectionalStreamId(
+        connection_->transport_version(), n);
   }
 
   QuicTransportVersion transport_version() const {
@@ -188,6 +188,7 @@
     EXPECT_CALL(owner_, OnStopSendingReceived(_)).Times(1);
     // Expect the RESET_STREAM that is generated in response to receiving a
     // STOP_SENDING.
+    EXPECT_CALL(*connection_, SendControlFrame(_));
     EXPECT_CALL(*connection_, OnStreamReset(stream_id, rst_stream_code));
     session_->OnStopSendingFrame(stop_sending);
   }
@@ -222,9 +223,8 @@
               reference.previous_connection_state());
 }
 
-INSTANTIATE_TEST_CASE_P(Tests,
-                        QuicServerSessionBaseTest,
-                        ::testing::ValuesIn(AllSupportedVersions()));
+INSTANTIATE_TEST_SUITE_P(Tests, QuicServerSessionBaseTest,
+                         ::testing::ValuesIn(AllSupportedVersions()));
 TEST_P(QuicServerSessionBaseTest, CloseStreamDueToReset) {
   // Open a stream, then reset it.
   // Send two bytes of payload to open it.
@@ -238,10 +238,14 @@
                           GetNthClientInitiatedBidirectionalId(0),
                           QUIC_ERROR_PROCESSING_STREAM, 0);
   EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
-  EXPECT_CALL(*connection_, SendControlFrame(_));
-  EXPECT_CALL(*connection_,
-              OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
-                            QUIC_RST_ACKNOWLEDGEMENT));
+  if (transport_version() != QUIC_VERSION_99) {
+    // For non-version 99, the RESET_STREAM will do the full close.
+    // Set up expects accordingly.
+    EXPECT_CALL(*connection_, SendControlFrame(_));
+    EXPECT_CALL(*connection_,
+                OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
+                              QUIC_RST_ACKNOWLEDGEMENT));
+  }
   visitor_->OnRstStream(rst1);
 
   // For version-99 will create and receive a stop-sending, completing
@@ -265,10 +269,14 @@
                           GetNthClientInitiatedBidirectionalId(0),
                           QUIC_ERROR_PROCESSING_STREAM, 0);
   EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
-  EXPECT_CALL(*connection_, SendControlFrame(_));
-  EXPECT_CALL(*connection_,
-              OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
-                            QUIC_RST_ACKNOWLEDGEMENT));
+  if (transport_version() != QUIC_VERSION_99) {
+    // For non-version 99, the RESET_STREAM will do the full close.
+    // Set up expects accordingly.
+    EXPECT_CALL(*connection_, SendControlFrame(_));
+    EXPECT_CALL(*connection_,
+                OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
+                              QUIC_RST_ACKNOWLEDGEMENT));
+  }
   visitor_->OnRstStream(rst1);
 
   // For version-99 will create and receive a stop-sending, completing
@@ -303,10 +311,14 @@
                          GetNthClientInitiatedBidirectionalId(0),
                          QUIC_ERROR_PROCESSING_STREAM, 0);
   EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
-  EXPECT_CALL(*connection_, SendControlFrame(_));
-  EXPECT_CALL(*connection_,
-              OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
-                            QUIC_RST_ACKNOWLEDGEMENT));
+  if (transport_version() != QUIC_VERSION_99) {
+    // For non-version 99, the RESET_STREAM will do the full close.
+    // Set up expects accordingly.
+    EXPECT_CALL(*connection_, SendControlFrame(_));
+    EXPECT_CALL(*connection_,
+                OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
+                              QUIC_RST_ACKNOWLEDGEMENT));
+  }
   visitor_->OnRstStream(rst);
 
   // For version-99 will create and receive a stop-sending, completing
@@ -350,7 +362,7 @@
   for (size_t i = 0; i < kMaxStreamsForTest; ++i) {
     EXPECT_TRUE(QuicServerSessionBasePeer::GetOrCreateDynamicStream(
         session_.get(), stream_id));
-    stream_id += QuicSpdySessionPeer::StreamIdDelta(*session_);
+    stream_id += QuicUtils::StreamIdDelta(connection_->transport_version());
   }
 
   if (transport_version() != QUIC_VERSION_99) {
@@ -359,11 +371,11 @@
     for (size_t i = 0; i < kMaxStreamsMinimumIncrement; ++i) {
       EXPECT_TRUE(QuicServerSessionBasePeer::GetOrCreateDynamicStream(
           session_.get(), stream_id));
-      stream_id += QuicSpdySessionPeer::StreamIdDelta(*session_);
+      stream_id += QuicUtils::StreamIdDelta(connection_->transport_version());
     }
   }
   // Now violate the server's internal stream limit.
-  stream_id += QuicSpdySessionPeer::StreamIdDelta(*session_);
+  stream_id += QuicUtils::StreamIdDelta(connection_->transport_version());
 
   if (transport_version() != QUIC_VERSION_99) {
     // For non-version 99, QUIC responds to an attempt to exceed the stream
@@ -395,7 +407,8 @@
       session_.get(), GetNthClientInitiatedBidirectionalId(0)));
 
   // Establish available streams up to the server's limit.
-  QuicStreamId next_id = QuicSpdySessionPeer::StreamIdDelta(*session_);
+  QuicStreamId next_id =
+      QuicUtils::StreamIdDelta(connection_->transport_version());
   const int kLimitingStreamId =
       GetNthClientInitiatedBidirectionalId(kAvailableStreamLimit + 1);
   if (transport_version() != QUIC_VERSION_99) {
@@ -665,9 +678,8 @@
   QuicCryptoServerConfigPeer crypto_config_peer_;
 };
 
-INSTANTIATE_TEST_CASE_P(StreamMemberLifetimeTests,
-                        StreamMemberLifetimeTest,
-                        ::testing::ValuesIn(AllSupportedVersions()));
+INSTANTIATE_TEST_SUITE_P(StreamMemberLifetimeTests, StreamMemberLifetimeTest,
+                         ::testing::ValuesIn(AllSupportedVersions()));
 
 // Trigger an operation which causes an async invocation of
 // ProofSource::GetProof.  Delay the completion of the operation until after the
diff --git a/quic/core/http/quic_spdy_client_session_test.cc b/quic/core/http/quic_spdy_client_session_test.cc
index a9d6c62..84229fd 100644
--- a/quic/core/http/quic_spdy_client_session_test.cc
+++ b/quic/core/http/quic_spdy_client_session_test.cc
@@ -108,12 +108,10 @@
     push_promise_[":method"] = "GET";
     push_promise_[":scheme"] = "https";
     promise_url_ = SpdyUtils::GetPromisedUrlFromHeaders(push_promise_);
-    promised_stream_id_ =
-        QuicSpdySessionPeer::GetNthServerInitiatedUnidirectionalStreamId(
-            *session_, 0);
-    associated_stream_id_ =
-        QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
-            *session_, 0);
+    promised_stream_id_ = GetNthServerInitiatedUnidirectionalStreamId(
+        connection_->transport_version(), 0);
+    associated_stream_id_ = GetNthClientInitiatedBidirectionalStreamId(
+        connection_->transport_version(), 0);
   }
 
   // The function ensures that A) the max stream id frames get properly deleted
@@ -172,9 +170,8 @@
   QuicStreamId associated_stream_id_;
 };
 
-INSTANTIATE_TEST_CASE_P(Tests,
-                        QuicSpdyClientSessionTest,
-                        ::testing::ValuesIn(AllSupportedVersions()));
+INSTANTIATE_TEST_SUITE_P(Tests, QuicSpdyClientSessionTest,
+                         ::testing::ValuesIn(AllSupportedVersions()));
 
 TEST_P(QuicSpdyClientSessionTest, CryptoConnect) {
   CompleteCryptoHandshake();
@@ -281,9 +278,8 @@
 
     // Note that this is to be the second stream created, but GetNth... starts
     // numbering at 0 (the first stream is 0, second is 1...)
-    QuicMaxStreamIdFrame frame(
-        0, QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
-               *session_, 1));
+    QuicMaxStreamIdFrame frame(0, GetNthClientInitiatedBidirectionalStreamId(
+                                      connection_->transport_version(), 1));
     session_->OnMaxStreamIdFrame(frame);
   }
   stream = session_->CreateOutgoingBidirectionalStream();
@@ -350,9 +346,8 @@
   if (GetParam().transport_version == QUIC_VERSION_99) {
     // Note that this is to be the second stream created, but GetNth... starts
     // numbering at 0 (the first stream is 0, second is 1...)
-    QuicMaxStreamIdFrame frame(
-        0, QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
-               *session_, 1));
+    QuicMaxStreamIdFrame frame(0, GetNthClientInitiatedBidirectionalStreamId(
+                                      connection_->transport_version(), 1));
     session_->OnMaxStreamIdFrame(frame);
   }
   stream = session_->CreateOutgoingBidirectionalStream();
@@ -454,7 +449,7 @@
   QuicReceivedPacket valid_packet(buf, 2, QuicTime::Zero(), false);
   // Close connection shouldn't be called.
   EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
-  if (connection_->transport_version() > QUIC_VERSION_46) {
+  if (connection_->transport_version() > QUIC_VERSION_44) {
     // Illegal fixed bit value.
     EXPECT_CALL(*connection_, OnError(_)).Times(1);
   }
@@ -539,7 +534,8 @@
   EXPECT_CALL(*stream, OnPromiseHeaderList(promised_stream_id_, _, _));
   session_->OnPromiseHeaderList(associated_stream_id_, promised_stream_id_, 0,
                                 QuicHeaderList());
-  associated_stream_id_ += QuicSpdySessionPeer::StreamIdDelta(*session_);
+  associated_stream_id_ +=
+      QuicUtils::StreamIdDelta(connection_->transport_version());
   EXPECT_CALL(*connection_,
               CloseConnection(QUIC_INVALID_STREAM_ID,
                               "Received push stream id lesser or equal to the"
@@ -557,9 +553,8 @@
       session_->CreateOutgoingBidirectionalStream());
 
   // Promise an illegal (outgoing) stream id.
-  promised_stream_id_ =
-      QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(*session_,
-                                                                      0);
+  promised_stream_id_ = GetNthClientInitiatedBidirectionalStreamId(
+      connection_->transport_version(), 0);
   EXPECT_CALL(
       *connection_,
       CloseConnection(QUIC_INVALID_STREAM_ID,
@@ -615,7 +610,8 @@
   EXPECT_NE(session_->GetPromisedById(promised_stream_id_), nullptr);
   EXPECT_NE(session_->GetPromisedByUrl(promise_url_), nullptr);
 
-  promised_stream_id_ += QuicSpdySessionPeer::StreamIdDelta(*session_);
+  promised_stream_id_ +=
+      QuicUtils::StreamIdDelta(connection_->transport_version());
   EXPECT_CALL(*connection_, SendControlFrame(_));
   EXPECT_CALL(*connection_,
               OnStreamReset(promised_stream_id_, QUIC_DUPLICATE_PROMISE_URL));
@@ -632,7 +628,8 @@
     push_promise_[":path"] = QuicStringPrintf("/bar%zu", i);
 
     QuicStreamId id =
-        promised_stream_id_ + i * QuicSpdySessionPeer::StreamIdDelta(*session_);
+        promised_stream_id_ +
+        i * QuicUtils::StreamIdDelta(connection_->transport_version());
 
     EXPECT_TRUE(
         session_->HandlePromised(associated_stream_id_, id, push_promise_));
@@ -648,7 +645,8 @@
   push_promise_[":path"] = QuicStringPrintf("/bar%d", i);
 
   QuicStreamId id =
-      promised_stream_id_ + i * QuicSpdySessionPeer::StreamIdDelta(*session_);
+      promised_stream_id_ +
+      i * QuicUtils::StreamIdDelta(connection_->transport_version());
   EXPECT_CALL(*connection_, SendControlFrame(_));
   EXPECT_CALL(*connection_, OnStreamReset(id, QUIC_REFUSED_STREAM));
   EXPECT_FALSE(
@@ -780,9 +778,8 @@
   } else {
     EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
   }
-  session_->GetOrCreateStream(
-      QuicSpdySessionPeer::GetNthServerInitiatedBidirectionalStreamId(*session_,
-                                                                      0));
+  session_->GetOrCreateStream(GetNthServerInitiatedBidirectionalStreamId(
+      connection_->transport_version(), 0));
 }
 
 }  // namespace
diff --git a/quic/core/http/quic_spdy_client_stream.cc b/quic/core/http/quic_spdy_client_stream.cc
index 21a09d6..d4e8a69 100644
--- a/quic/core/http/quic_spdy_client_stream.cc
+++ b/quic/core/http/quic_spdy_client_stream.cc
@@ -151,7 +151,7 @@
   bytes_sent += header_bytes_written_;
 
   if (!body.empty()) {
-    WriteOrBufferBody(body, fin, nullptr);
+    WriteOrBufferBody(body, fin);
   }
 
   return bytes_sent;
diff --git a/quic/core/http/quic_spdy_client_stream_test.cc b/quic/core/http/quic_spdy_client_stream_test.cc
index ded2d7d..059558b 100644
--- a/quic/core/http/quic_spdy_client_stream_test.cc
+++ b/quic/core/http/quic_spdy_client_stream_test.cc
@@ -75,8 +75,8 @@
     headers_["content-length"] = "11";
 
     stream_ = QuicMakeUnique<QuicSpdyClientStream>(
-        QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
-            session_, 0),
+        GetNthClientInitiatedBidirectionalStreamId(
+            connection_->transport_version(), 0),
         &session_, BIDIRECTIONAL);
     stream_visitor_ = QuicMakeUnique<StreamVisitor>();
     stream_->set_visitor(stream_visitor_.get());
@@ -101,9 +101,8 @@
   HttpEncoder encoder_;
 };
 
-INSTANTIATE_TEST_CASE_P(Tests,
-                        QuicSpdyClientStreamTest,
-                        ::testing::ValuesIn(AllSupportedVersions()));
+INSTANTIATE_TEST_SUITE_P(Tests, QuicSpdyClientStreamTest,
+                         ::testing::ValuesIn(AllSupportedVersions()));
 
 TEST_P(QuicSpdyClientStreamTest, TestReceivingIllegalResponseStatusCode) {
   headers_[":status"] = "200 ok";
@@ -125,7 +124,7 @@
   QuicByteCount header_length =
       encoder_.SerializeDataFrameHeader(body_.length(), &buffer);
   QuicString header = QuicString(buffer.get(), header_length);
-  QuicString data = connection_->transport_version() == QUIC_VERSION_99
+  QuicString data = VersionHasDataFrameHeader(connection_->transport_version())
                         ? header + body_
                         : body_;
   stream_->OnStreamFrame(
@@ -156,7 +155,7 @@
   QuicByteCount header_length =
       encoder_.SerializeDataFrameHeader(body_.length(), &buffer);
   QuicString header = QuicString(buffer.get(), header_length);
-  QuicString data = connection_->transport_version() == QUIC_VERSION_99
+  QuicString data = VersionHasDataFrameHeader(connection_->transport_version())
                         ? header + body_
                         : body_;
   stream_->OnStreamFrame(
@@ -180,7 +179,7 @@
   QuicByteCount header_length =
       encoder_.SerializeDataFrameHeader(large_body.length(), &buffer);
   QuicString header = QuicString(buffer.get(), header_length);
-  QuicString data = connection_->transport_version() == QUIC_VERSION_99
+  QuicString data = VersionHasDataFrameHeader(connection_->transport_version())
                         ? header + large_body
                         : large_body;
   EXPECT_CALL(*connection_, SendControlFrame(_));
@@ -219,7 +218,7 @@
   QuicByteCount header_length =
       encoder_.SerializeDataFrameHeader(body_.length(), &buffer);
   QuicString header = QuicString(buffer.get(), header_length);
-  QuicString data = connection_->transport_version() == QUIC_VERSION_99
+  QuicString data = VersionHasDataFrameHeader(connection_->transport_version())
                         ? header + body_
                         : body_;
   stream_->OnStreamFrame(
diff --git a/quic/core/http/quic_spdy_server_stream_base_test.cc b/quic/core/http/quic_spdy_server_stream_base_test.cc
index 51abf0c..4e1aa1b 100644
--- a/quic/core/http/quic_spdy_server_stream_base_test.cc
+++ b/quic/core/http/quic_spdy_server_stream_base_test.cc
@@ -32,8 +32,8 @@
                                         &alarm_factory_,
                                         Perspective::IS_SERVER)) {
     stream_ = new TestQuicSpdyServerStream(
-        QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
-            session_, 0),
+        GetNthClientInitiatedBidirectionalStreamId(
+            session_.connection()->transport_version(), 0),
         &session_, BIDIRECTIONAL);
     session_.ActivateStream(QuicWrapUnique(stream_));
     helper_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
@@ -58,7 +58,21 @@
   EXPECT_FALSE(stream_->reading_stopped());
 
   EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0);
-  EXPECT_CALL(session_, SendRstStream(_, QUIC_RST_ACKNOWLEDGEMENT, _)).Times(1);
+
+  if (session_.connection()->transport_version() != QUIC_VERSION_99) {
+    EXPECT_CALL(session_, SendRstStream(_, QUIC_RST_ACKNOWLEDGEMENT, _))
+        .Times(1);
+  } else {
+    // Intercept & check that the call to the QuicConnection's OnStreamReast
+    // has correct stream ID and error code -- for V99/IETF Quic, it should
+    // have the STREAM_CANCELLED error code, not RST_ACK... Capture
+    // OnStreamReset (rather than SendRstStream) because the V99 path bypasses
+    // SendRstStream, calling SendRstStreamInner directly. Mocking
+    // SendRstStreamInner is problematic since the test relies on it to perform
+    // the closing operations and getting the stream in the correct state.
+    EXPECT_CALL(*(static_cast<MockQuicConnection*>(session_.connection())),
+                OnStreamReset(stream_->id(), QUIC_STREAM_CANCELLED));
+  }
   QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(),
                                QUIC_STREAM_CANCELLED, 1234);
   stream_->OnStreamReset(rst_frame);
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc
index bc3cf5c..824ecfa 100644
--- a/quic/core/http/quic_spdy_session.cc
+++ b/quic/core/http/quic_spdy_session.cc
@@ -347,6 +347,11 @@
               QuicUtils::GetHeadersStreamId(connection()->transport_version()));
   }
 
+  if (VersionUsesQpack(connection()->transport_version())) {
+    qpack_encoder_ = QuicMakeUnique<QpackEncoder>(this, this);
+    qpack_decoder_ = QuicMakeUnique<QpackDecoder>(this, this);
+  }
+
   headers_stream_ = QuicMakeUnique<QuicHeadersStream>((this));
   DCHECK_EQ(QuicUtils::GetHeadersStreamId(connection()->transport_version()),
             headers_stream_->id());
@@ -360,6 +365,34 @@
   set_max_decode_buffer_size_bytes(2 * max_inbound_header_list_size_);
 }
 
+void QuicSpdySession::OnDecoderStreamError(QuicStringPiece error_message) {
+  DCHECK(VersionUsesQpack(connection()->transport_version()));
+
+  // TODO(112770235): Signal connection error on decoder stream errors.
+  QUIC_NOTREACHED();
+}
+
+void QuicSpdySession::WriteEncoderStreamData(QuicStringPiece data) {
+  DCHECK(VersionUsesQpack(connection()->transport_version()));
+
+  // TODO(112770235): Send encoder stream data on encoder stream.
+  QUIC_NOTREACHED();
+}
+
+void QuicSpdySession::OnEncoderStreamError(QuicStringPiece error_message) {
+  DCHECK(VersionUsesQpack(connection()->transport_version()));
+
+  // TODO(112770235): Signal connection error on encoder stream errors.
+  QUIC_NOTREACHED();
+}
+
+void QuicSpdySession::WriteDecoderStreamData(QuicStringPiece data) {
+  DCHECK(VersionUsesQpack(connection()->transport_version()));
+
+  // TODO(112770235): Send decoder stream data on decoder stream.
+  QUIC_NOTREACHED();
+}
+
 void QuicSpdySession::OnStreamHeadersPriority(QuicStreamId stream_id,
                                               SpdyPriority priority) {
   QuicSpdyStream* stream = GetSpdyDataStream(stream_id);
@@ -424,38 +457,16 @@
                                    iov.iov_len);
 }
 
-size_t QuicSpdySession::WriteHeaders(
+size_t QuicSpdySession::WriteHeadersOnHeadersStream(
     QuicStreamId id,
     SpdyHeaderBlock headers,
     bool fin,
     SpdyPriority priority,
     QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
-  return WriteHeadersImpl(
-      id, std::move(headers), fin, Spdy3PriorityToHttp2Weight(priority),
-      /*parent_stream_id=*/0, /*exclusive=*/false, std::move(ack_listener));
-}
-
-size_t QuicSpdySession::WriteHeadersImpl(
-    QuicStreamId id,
-    SpdyHeaderBlock headers,
-    bool fin,
-    int weight,
-    QuicStreamId parent_stream_id,
-    bool exclusive,
-    QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
-  SpdyHeadersIR headers_frame(id, std::move(headers));
-  headers_frame.set_fin(fin);
-  if (perspective() == Perspective::IS_CLIENT) {
-    headers_frame.set_has_priority(true);
-    headers_frame.set_weight(weight);
-    headers_frame.set_parent_stream_id(parent_stream_id);
-    headers_frame.set_exclusive(exclusive);
-  }
-  SpdySerializedFrame frame(spdy_framer_.SerializeFrame(headers_frame));
-  headers_stream_->WriteOrBufferData(
-      QuicStringPiece(frame.data(), frame.size()), false,
-      std::move(ack_listener));
-  return frame.size();
+  return WriteHeadersOnHeadersStreamImpl(
+      id, std::move(headers), fin,
+      /* parent_stream_id = */ 0, Spdy3PriorityToHttp2Weight(priority),
+      /* exclusive = */ false, std::move(ack_listener));
 }
 
 size_t QuicSpdySession::WritePriority(QuicStreamId id,
@@ -502,6 +513,18 @@
   return frame.size();
 }
 
+QpackEncoder* QuicSpdySession::qpack_encoder() {
+  DCHECK(VersionUsesQpack(connection()->transport_version()));
+
+  return qpack_encoder_.get();
+}
+
+QpackDecoder* QuicSpdySession::qpack_decoder() {
+  DCHECK(VersionUsesQpack(connection()->transport_version()));
+
+  return qpack_decoder_.get();
+}
+
 QuicSpdyStream* QuicSpdySession::GetSpdyDataStream(
     const QuicStreamId stream_id) {
   return static_cast<QuicSpdyStream*>(GetOrCreateDynamicStream(stream_id));
@@ -519,6 +542,29 @@
   return !QuicUtils::IsBidirectionalStreamId(id);
 }
 
+size_t QuicSpdySession::WriteHeadersOnHeadersStreamImpl(
+    QuicStreamId id,
+    spdy::SpdyHeaderBlock headers,
+    bool fin,
+    QuicStreamId parent_stream_id,
+    int weight,
+    bool exclusive,
+    QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+  SpdyHeadersIR headers_frame(id, std::move(headers));
+  headers_frame.set_fin(fin);
+  if (perspective() == Perspective::IS_CLIENT) {
+    headers_frame.set_has_priority(true);
+    headers_frame.set_parent_stream_id(parent_stream_id);
+    headers_frame.set_weight(weight);
+    headers_frame.set_exclusive(exclusive);
+  }
+  SpdySerializedFrame frame(spdy_framer_.SerializeFrame(headers_frame));
+  headers_stream_->WriteOrBufferData(
+      QuicStringPiece(frame.data(), frame.size()), false,
+      std::move(ack_listener));
+  return frame.size();
+}
+
 void QuicSpdySession::OnPromiseHeaderList(QuicStreamId stream_id,
                                           QuicStreamId promised_stream_id,
                                           size_t frame_len,
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h
index 2874e26..363edf3 100644
--- a/quic/core/http/quic_spdy_session.h
+++ b/quic/core/http/quic_spdy_session.h
@@ -12,7 +12,12 @@
 #include "net/third_party/quiche/src/quic/core/http/quic_header_list.h"
 #include "net/third_party/quiche/src/quic/core/http/quic_headers_stream.h"
 #include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h"
 #include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
@@ -44,7 +49,12 @@
 };
 
 // A QUIC session with a headers stream.
-class QUIC_EXPORT_PRIVATE QuicSpdySession : public QuicSession {
+class QUIC_EXPORT_PRIVATE QuicSpdySession
+    : public QuicSession,
+      public QpackEncoder::DecoderStreamErrorDelegate,
+      public QpackEncoderStreamSender::Delegate,
+      public QpackDecoder::EncoderStreamErrorDelegate,
+      public QpackDecoderStreamSender::Delegate {
  public:
   // Does not take ownership of |connection| or |visitor|.
   QuicSpdySession(QuicConnection* connection,
@@ -58,6 +68,18 @@
 
   void Initialize() override;
 
+  // QpackEncoder::DecoderStreamErrorDelegate implementation.
+  void OnDecoderStreamError(QuicStringPiece error_message) override;
+
+  // QpackEncoderStreamSender::Delegate implemenation.
+  void WriteEncoderStreamData(QuicStringPiece data) override;
+
+  // QpackDecoder::EncoderStreamErrorDelegate implementation.
+  void OnEncoderStreamError(QuicStringPiece error_message) override;
+
+  // QpackDecoderStreamSender::Delegate implementation.
+  void WriteDecoderStreamData(QuicStringPiece data) override;
+
   // Called by |headers_stream_| when headers with a priority have been
   // received for a stream.  This method will only be called for server streams.
   virtual void OnStreamHeadersPriority(QuicStreamId stream_id,
@@ -79,7 +101,7 @@
                                    size_t frame_len,
                                    const QuicHeaderList& header_list);
 
-  // Callbed by |headers_stream_| when a PRIORITY frame has been received for a
+  // Called by |headers_stream_| when a PRIORITY frame has been received for a
   // stream. This method will only be called for server streams.
   virtual void OnPriorityFrame(QuicStreamId stream_id,
                                spdy::SpdyPriority priority);
@@ -91,7 +113,7 @@
   // If |fin| is true, then no more data will be sent for the stream |id|.
   // If provided, |ack_notifier_delegate| will be registered to be notified when
   // we have seen ACKs for all packets resulting from this call.
-  virtual size_t WriteHeaders(
+  virtual size_t WriteHeadersOnHeadersStream(
       QuicStreamId id,
       spdy::SpdyHeaderBlock headers,
       bool fin,
@@ -116,6 +138,8 @@
   // Sends SETTINGS_MAX_HEADER_LIST_SIZE SETTINGS frame.
   size_t SendMaxHeaderListSize(size_t value);
 
+  QpackEncoder* qpack_encoder();
+  QpackDecoder* qpack_decoder();
   QuicHeadersStream* headers_stream() { return headers_stream_.get(); }
 
   bool server_push_enabled() const { return server_push_enabled_; }
@@ -157,15 +181,12 @@
   // Overridden to buffer incoming streams for version 99.
   bool ShouldBufferIncomingStream(QuicStreamId id) const override;
 
-  // This was formerly QuicHeadersStream::WriteHeaders.  Needs to be
-  // separate from QuicSpdySession::WriteHeaders because tests call
-  // this but mock the latter.
-  size_t WriteHeadersImpl(
+  size_t WriteHeadersOnHeadersStreamImpl(
       QuicStreamId id,
       spdy::SpdyHeaderBlock headers,
       bool fin,
-      int weight,
       QuicStreamId parent_stream_id,
+      int weight,
       bool exclusive,
       QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
 
@@ -226,6 +247,10 @@
   // Called when the size of the compressed frame payload is available.
   void OnCompressedFrameSize(size_t frame_len);
 
+  std::unique_ptr<QpackEncoder> qpack_encoder_;
+  std::unique_ptr<QpackDecoder> qpack_decoder_;
+
+  // TODO(123528590): Remove this member.
   std::unique_ptr<QuicHeadersStream> headers_stream_;
 
   // The maximum size of a header block that will be accepted from the peer,
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index affa07a..d9c4ab1 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -98,6 +98,8 @@
 
   MOCK_METHOD0(OnCanWrite, void());
 
+  bool HasPendingCryptoRetransmission() override { return false; }
+
   MOCK_CONST_METHOD0(HasPendingRetransmission, bool());
 
  private:
@@ -187,7 +189,8 @@
       TestStream* stream = new TestStream(
           id, this,
           DetermineStreamType(id, connection()->transport_version(),
-                              /*is_incoming=*/true, BIDIRECTIONAL));
+                              perspective(), /*is_incoming=*/true,
+                              BIDIRECTIONAL));
       ActivateStream(QuicWrapUnique(stream));
       return stream;
     }
@@ -195,10 +198,11 @@
 
   TestStream* CreateIncomingStream(PendingStream pending) override {
     QuicStreamId id = pending.id();
-    TestStream* stream = new TestStream(
-        std::move(pending), this,
-        DetermineStreamType(id, connection()->transport_version(),
-                            /*is_incoming=*/true, BIDIRECTIONAL));
+    TestStream* stream =
+        new TestStream(std::move(pending), this,
+                       DetermineStreamType(
+                           id, connection()->transport_version(), perspective(),
+                           /*is_incoming=*/true, BIDIRECTIONAL));
     ActivateStream(QuicWrapUnique(stream));
     return stream;
   }
@@ -342,7 +346,7 @@
   }
 
   void CloseStream(QuicStreamId id) {
-    if (transport_version() != QUIC_VERSION_99) {
+    if (!IsVersion99()) {
       EXPECT_CALL(*connection_, SendControlFrame(_))
           .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame));
     } else {
@@ -360,18 +364,19 @@
     return connection_->transport_version();
   }
 
+  bool IsVersion99() const { return transport_version() == QUIC_VERSION_99; }
+
   QuicStreamId GetNthClientInitiatedBidirectionalId(int n) {
-    return QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
-        session_, n);
+    return GetNthClientInitiatedBidirectionalStreamId(transport_version(), n);
   }
 
   QuicStreamId GetNthServerInitiatedBidirectionalId(int n) {
-    return QuicSpdySessionPeer::GetNthServerInitiatedBidirectionalStreamId(
-        session_, n);
+    return GetNthServerInitiatedBidirectionalStreamId(
+        connection_->transport_version(), n);
   }
 
   QuicStreamId IdDelta() {
-    return QuicSpdySessionPeer::StreamIdDelta(session_);
+    return QuicUtils::StreamIdDelta(connection_->transport_version());
   }
 
   MockQuicConnectionHelper helper_;
@@ -388,12 +393,12 @@
       : QuicSpdySessionTestBase(Perspective::IS_SERVER) {}
 };
 
-INSTANTIATE_TEST_CASE_P(Tests,
-                        QuicSpdySessionTestServer,
-                        ::testing::ValuesIn(AllSupportedVersions()));
+INSTANTIATE_TEST_SUITE_P(Tests,
+                         QuicSpdySessionTestServer,
+                         ::testing::ValuesIn(AllSupportedVersions()));
 
 TEST_P(QuicSpdySessionTestServer, ShouldBufferIncomingStreamUnidirectional) {
-  if (connection_->transport_version() != QUIC_VERSION_99) {
+  if (!IsVersion99()) {
     return;
   }
   EXPECT_TRUE(session_.ShouldBufferIncomingStream(
@@ -402,7 +407,7 @@
 }
 
 TEST_P(QuicSpdySessionTestServer, ShouldBufferIncomingStreamBidirectional) {
-  if (connection_->transport_version() != QUIC_VERSION_99) {
+  if (!IsVersion99()) {
     return;
   }
   EXPECT_FALSE(session_.ShouldBufferIncomingStream(
@@ -416,7 +421,7 @@
 }
 
 TEST_P(QuicSpdySessionTestServer, SelfAddress) {
-  EXPECT_EQ(QuicSocketAddress(), session_.self_address());
+  EXPECT_TRUE(session_.self_address().IsInitialized());
 }
 
 TEST_P(QuicSpdySessionTestServer, IsCryptoHandshakeConfirmed) {
@@ -481,7 +486,7 @@
 }
 
 TEST_P(QuicSpdySessionTestServer, MaximumAvailableOpenedStreams) {
-  if (transport_version() == QUIC_VERSION_99) {
+  if (IsVersion99()) {
     // For IETF QUIC, we should be able to obtain the max allowed
     // stream ID, the next ID should fail. Since the actual limit
     // is not the number of open streams, we allocate the max and the max+2.
@@ -532,7 +537,7 @@
   // A stream ID which is too large to create.
   stream_id2 = GetNthClientInitiatedBidirectionalId(
       2 * session_.MaxAvailableBidirectionalStreams() + 4);
-  if (transport_version() == QUIC_VERSION_99) {
+  if (IsVersion99()) {
     EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _));
   } else {
     EXPECT_CALL(*connection_,
@@ -670,7 +675,7 @@
 }
 
 TEST_P(QuicSpdySessionTestServer, OnCanWriteBundlesStreams) {
-  if (transport_version() == QUIC_VERSION_99) {
+  if (IsVersion99()) {
     EXPECT_CALL(*connection_, SendControlFrame(_))
         .WillRepeatedly(Invoke(
             this, &QuicSpdySessionTestServer::ClearMaxStreamIdControlFrame));
@@ -776,8 +781,6 @@
   MockPacketWriter* writer = static_cast<MockPacketWriter*>(
       QuicConnectionPeer::GetWriter(session_.connection()));
   EXPECT_CALL(*writer, IsWriteBlocked()).WillRepeatedly(Return(true));
-  EXPECT_CALL(*writer, IsWriteBlockedDataBuffered())
-      .WillRepeatedly(Return(true));
   EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)).Times(0);
 
   TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
@@ -908,7 +911,7 @@
 }
 
 TEST_P(QuicSpdySessionTestServer, SendGoAway) {
-  if (transport_version() == QUIC_VERSION_99) {
+  if (IsVersion99()) {
     // GoAway frames are not in version 99
     return;
   }
@@ -932,7 +935,7 @@
 }
 
 TEST_P(QuicSpdySessionTestServer, DoNotSendGoAwayTwice) {
-  if (connection_->transport_version() == QUIC_VERSION_99) {
+  if (IsVersion99()) {
     // TODO(b/118808809): Enable this test for version 99 when GOAWAY is
     // supported.
     return;
@@ -945,7 +948,7 @@
 }
 
 TEST_P(QuicSpdySessionTestServer, InvalidGoAway) {
-  if (connection_->transport_version() == QUIC_VERSION_99) {
+  if (IsVersion99()) {
     // TODO(b/118808809): Enable this test for version 99 when GOAWAY is
     // supported.
     return;
@@ -967,7 +970,7 @@
 
   EXPECT_CALL(*connection_,
               SendConnectivityProbingResponsePacket(new_peer_address));
-  if (transport_version() == QUIC_VERSION_99) {
+  if (IsVersion99()) {
     // Need to explicitly do this to emulate the reception of a PathChallenge,
     // which stores its payload for use in generating the response.
     connection_->OnPathChallengeFrame(
@@ -995,8 +998,12 @@
   EXPECT_EQ(1u, session_.GetNumOpenIncomingStreams());
 
   EXPECT_CALL(*connection_, SendControlFrame(_));
-  EXPECT_CALL(*connection_,
-              OnStreamReset(GetNthClientInitiatedBidirectionalId(0), _));
+  if (!IsVersion99()) {
+    // For version99, OnStreamReset gets called because of the STOP_SENDING,
+    // below. EXPECT the call there.
+    EXPECT_CALL(*connection_,
+                OnStreamReset(GetNthClientInitiatedBidirectionalId(0), _));
+  }
   QuicRstStreamFrame rst1(kInvalidControlFrameId,
                           GetNthClientInitiatedBidirectionalId(0),
                           QUIC_ERROR_PROCESSING_STREAM, 0);
@@ -1005,7 +1012,7 @@
   // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a
   // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes a
   // one-way close.
-  if (transport_version() == QUIC_VERSION_99) {
+  if (IsVersion99()) {
     // Only needed for version 99/IETF QUIC.
     QuicStopSendingFrame stop_sending(
         kInvalidControlFrameId, GetNthClientInitiatedBidirectionalId(0),
@@ -1088,7 +1095,7 @@
   EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
   EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
   EXPECT_CALL(*connection_, SendControlFrame(_)).Times(AtLeast(1));
-  stream2->WriteOrBufferBody(body, false, nullptr);
+  stream2->WriteOrBufferBody(body, false);
   EXPECT_TRUE(stream2->flow_controller()->IsBlocked());
   EXPECT_TRUE(session_.IsConnectionFlowControlBlocked());
   EXPECT_TRUE(session_.IsStreamFlowControlBlocked());
@@ -1106,6 +1113,11 @@
 
 TEST_P(QuicSpdySessionTestServer,
        HandshakeUnblocksFlowControlBlockedCryptoStream) {
+  if (GetParam().transport_version >= QUIC_VERSION_47) {
+    // QUIC version 47 onwards uses CRYPTO frames for the handshake, so this
+    // test doesn't make sense for those versions.
+    return;
+  }
   // Test that if the crypto stream is flow control blocked, then if the SHLO
   // contains a larger send window offset, the stream becomes unblocked.
   session_.set_writev_consumes_all_data(true);
@@ -1118,10 +1130,9 @@
   EXPECT_FALSE(headers_stream->flow_controller()->IsBlocked());
   EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
   EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
-  if (transport_version() == QUIC_VERSION_99) {
+  if (IsVersion99()) {
     EXPECT_CALL(*connection_, SendControlFrame(_))
-        .Times(1)
-        .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame));
+        .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame));
   } else {
     EXPECT_CALL(*connection_, SendControlFrame(_))
         .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame));
@@ -1190,12 +1201,14 @@
     EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
     headers["header"] = QuicStrCat(random.RandUint64(), random.RandUint64(),
                                    random.RandUint64());
-    session_.WriteHeaders(stream_id, headers.Clone(), true, 0, nullptr);
+    session_.WriteHeadersOnHeadersStream(stream_id, headers.Clone(), true, 0,
+                                         nullptr);
     stream_id += IdDelta();
   }
   // Write once more to ensure that the headers stream has buffered data. The
   // random headers may have exactly filled the flow control window.
-  session_.WriteHeaders(stream_id, std::move(headers), true, 0, nullptr);
+  session_.WriteHeadersOnHeadersStream(stream_id, std::move(headers), true, 0,
+                                       nullptr);
   EXPECT_TRUE(headers_stream->HasBufferedData());
 
   EXPECT_TRUE(headers_stream->flow_controller()->IsBlocked());
@@ -1231,25 +1244,21 @@
   const QuicStreamOffset kByteOffset =
       1 + kInitialSessionFlowControlWindowForTest / 2;
 
-  if (transport_version() != QUIC_VERSION_99) {
-    EXPECT_CALL(*connection_, SendControlFrame(_))
-        .Times(2)
-        .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame));
-  } else {
-    // V99 has an additional, STOP_SENDING, frame and an additional RST_STREAM
-    // (the response to the STOP_SENDING) frame.
-    EXPECT_CALL(*connection_, SendControlFrame(_))
-        .Times(4)
-        .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame));
+  EXPECT_CALL(*connection_, SendControlFrame(_))
+      .Times(2)
+      .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame));
+  if (!IsVersion99()) {
+    // For version99 the call to OnStreamReset happens as a result of receiving
+    // the STOP_SENDING, so set up the EXPECT there.
+    EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _));
   }
-  EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _));
   QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream->id(),
                                QUIC_STREAM_CANCELLED, kByteOffset);
   session_.OnRstStream(rst_frame);
   // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a
   // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes a
   // one-way close.
-  if (transport_version() == QUIC_VERSION_99) {
+  if (IsVersion99()) {
     // Only needed for version 99/IETF QUIC.
     QuicStopSendingFrame stop_sending(
         kInvalidControlFrameId, stream->id(),
@@ -1457,12 +1466,13 @@
       GetNthClientInitiatedBidirectionalId(kMaxStreams);
   // Create kMaxStreams data streams, and close them all without receiving a
   // FIN or a RST_STREAM from the client.
-  const QuicStreamId kNextId = QuicSpdySessionPeer::StreamIdDelta(session_);
+  const QuicStreamId kNextId =
+      QuicUtils::StreamIdDelta(connection_->transport_version());
   for (QuicStreamId i = kFirstStreamId; i < kFinalStreamId; i += kNextId) {
     QuicStreamFrame data1(i, false, 0, QuicStringPiece("HT"));
     session_.OnStreamFrame(data1);
     // EXPECT_EQ(1u, session_.GetNumOpenStreams());
-    if (transport_version() != QUIC_VERSION_99) {
+    if (!IsVersion99()) {
       EXPECT_CALL(*connection_, SendControlFrame(_))
           .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame));
     } else {
@@ -1478,7 +1488,7 @@
     session_.CloseStream(i);
   }
   // Try and open a stream that exceeds the limit.
-  if (transport_version() != QUIC_VERSION_99) {
+  if (!IsVersion99()) {
     // On versions other than 99, opening such a stream results in a
     // RST_STREAM.
     EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1);
@@ -1499,7 +1509,7 @@
   // Verify that a draining stream (which has received a FIN but not consumed
   // it) does not count against the open quota (because it is closed from the
   // protocol point of view).
-  if (transport_version() == QUIC_VERSION_99) {
+  if (IsVersion99()) {
     // Version 99 will result in a MAX_STREAM_ID frame as streams are consumed
     // (via the OnStreamFrame call) and then released (via
     // StreamDraining). Eventually this node will believe that the peer is
@@ -1532,9 +1542,9 @@
       : QuicSpdySessionTestBase(Perspective::IS_CLIENT) {}
 };
 
-INSTANTIATE_TEST_CASE_P(Tests,
-                        QuicSpdySessionTestClient,
-                        ::testing::ValuesIn(AllSupportedVersions()));
+INSTANTIATE_TEST_SUITE_P(Tests,
+                         QuicSpdySessionTestClient,
+                         ::testing::ValuesIn(AllSupportedVersions()));
 
 TEST_P(QuicSpdySessionTestClient, AvailableStreamsClient) {
   ASSERT_TRUE(session_.GetOrCreateDynamicStream(
@@ -1658,11 +1668,18 @@
 
   // Lost data on cryption stream, streams 2 and 4.
   EXPECT_CALL(*stream4, HasPendingRetransmission()).WillOnce(Return(true));
-  EXPECT_CALL(*crypto_stream, HasPendingRetransmission())
-      .WillOnce(Return(true));
+  if (connection_->transport_version() < QUIC_VERSION_47) {
+    EXPECT_CALL(*crypto_stream, HasPendingRetransmission())
+        .WillOnce(Return(true));
+  }
   EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(true));
   session_.OnFrameLost(QuicFrame(frame3));
-  session_.OnFrameLost(QuicFrame(frame1));
+  if (connection_->transport_version() < QUIC_VERSION_47) {
+    session_.OnFrameLost(QuicFrame(frame1));
+  } else {
+    QuicCryptoFrame crypto_frame(ENCRYPTION_NONE, 0, 1300);
+    session_.OnFrameLost(QuicFrame(&crypto_frame));
+  }
   session_.OnFrameLost(QuicFrame(frame2));
   EXPECT_TRUE(session_.WillingAndAbleToWrite());
 
@@ -1674,9 +1691,11 @@
   // stream go first.
   // Do not check congestion window when crypto stream has lost data.
   EXPECT_CALL(*send_algorithm, CanSend(_)).Times(0);
-  EXPECT_CALL(*crypto_stream, OnCanWrite());
-  EXPECT_CALL(*crypto_stream, HasPendingRetransmission())
-      .WillOnce(Return(false));
+  if (connection_->transport_version() < QUIC_VERSION_47) {
+    EXPECT_CALL(*crypto_stream, OnCanWrite());
+    EXPECT_CALL(*crypto_stream, HasPendingRetransmission())
+        .WillOnce(Return(false));
+  }
   // Check congestion window for non crypto streams.
   EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true));
   EXPECT_CALL(*stream4, OnCanWrite());
diff --git a/quic/core/http/quic_spdy_stream.cc b/quic/core/http/quic_spdy_stream.cc
index eb8135a..9e9f1b7 100644
--- a/quic/core/http/quic_spdy_stream.cc
+++ b/quic/core/http/quic_spdy_stream.cc
@@ -81,7 +81,9 @@
     CloseConnectionOnWrongFrame("Headers");
   }
 
-  void OnHeadersFrameEnd() override { CloseConnectionOnWrongFrame("Headers"); }
+  void OnHeadersFrameEnd(QuicByteCount frame_len) override {
+    CloseConnectionOnWrongFrame("Headers");
+  }
 
   void OnPushPromiseFrameStart(PushId push_id) override {
     CloseConnectionOnWrongFrame("Push Promise");
@@ -121,7 +123,7 @@
       trailers_consumed_(false),
       http_decoder_visitor_(new HttpDecoderVisitor(this)),
       body_buffer_(sequencer()),
-      total_header_bytes_written_(0) {
+      ack_listener_(nullptr) {
   DCHECK_NE(QuicUtils::GetCryptoStreamId(
                 spdy_session->connection()->transport_version()),
             id);
@@ -129,7 +131,8 @@
   // are complete.
   sequencer()->SetBlockedUntilFlush();
 
-  if (spdy_session_->connection()->transport_version() == QUIC_VERSION_99) {
+  if (VersionHasDataFrameHeader(
+          spdy_session_->connection()->transport_version())) {
     sequencer()->set_level_triggered(true);
   }
   decoder_.set_visitor(http_decoder_visitor_.get());
@@ -146,7 +149,7 @@
       trailers_consumed_(false),
       http_decoder_visitor_(new HttpDecoderVisitor(this)),
       body_buffer_(sequencer()),
-      total_header_bytes_written_(0) {
+      ack_listener_(nullptr) {
   DCHECK_NE(QuicUtils::GetCryptoStreamId(
                 spdy_session->connection()->transport_version()),
             id());
@@ -154,7 +157,8 @@
   // are complete.
   sequencer()->SetBlockedUntilFlush();
 
-  if (spdy_session_->connection()->transport_version() == QUIC_VERSION_99) {
+  if (VersionHasDataFrameHeader(
+          spdy_session_->connection()->transport_version())) {
     sequencer()->set_level_triggered(true);
   }
   decoder_.set_visitor(http_decoder_visitor_.get());
@@ -166,8 +170,8 @@
     SpdyHeaderBlock header_block,
     bool fin,
     QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
-  size_t bytes_written = spdy_session_->WriteHeaders(
-      id(), std::move(header_block), fin, priority(), std::move(ack_listener));
+  size_t bytes_written =
+      WriteHeadersImpl(std::move(header_block), fin, std::move(ack_listener));
   if (fin) {
     // TODO(rch): Add test to ensure fin_sent_ is set whenever a fin is sent.
     set_fin_sent(true);
@@ -176,28 +180,32 @@
   return bytes_written;
 }
 
-void QuicSpdyStream::WriteOrBufferBody(
-    QuicStringPiece data,
-    bool fin,
-    QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
-  if (spdy_session_->connection()->transport_version() != QUIC_VERSION_99 ||
+void QuicSpdyStream::WriteOrBufferBody(QuicStringPiece data, bool fin) {
+  if (!VersionHasDataFrameHeader(
+          spdy_session_->connection()->transport_version()) ||
       data.length() == 0) {
-    WriteOrBufferData(data, fin, std::move(ack_listener));
+    WriteOrBufferData(data, fin, nullptr);
     return;
   }
   QuicConnection::ScopedPacketFlusher flusher(
       spdy_session_->connection(), QuicConnection::SEND_ACK_IF_PENDING);
+
+  // Write frame header.
   std::unique_ptr<char[]> buffer;
   QuicByteCount header_length =
       encoder_.SerializeDataFrameHeader(data.length(), &buffer);
-  WriteOrBufferData(QuicStringPiece(buffer.get(), header_length), false,
-                    nullptr);
+  unacked_frame_headers_offsets_.Add(
+      send_buffer().stream_offset(),
+      send_buffer().stream_offset() + header_length);
   QUIC_DLOG(INFO) << "Stream " << id() << " is writing header of length "
                   << header_length;
-  total_header_bytes_written_ += header_length;
-  WriteOrBufferData(data, fin, std::move(ack_listener));
+  WriteOrBufferData(QuicStringPiece(buffer.get(), header_length), false,
+                    nullptr);
+
+  // Write body.
   QUIC_DLOG(INFO) << "Stream " << id() << " is writing body of length "
                   << data.length();
+  WriteOrBufferData(data, fin, nullptr);
 }
 
 size_t QuicSpdyStream::WriteTrailers(
@@ -221,8 +229,7 @@
   // trailers are the last thing to be sent on a stream.
   const bool kFin = true;
   size_t bytes_written =
-      spdy_session_->WriteHeaders(id(), std::move(trailer_block), kFin,
-                                  priority(), std::move(ack_listener));
+      WriteHeadersImpl(std::move(trailer_block), kFin, std::move(ack_listener));
   set_fin_sent(kFin);
 
   // Trailers are the last thing to be sent on a stream, but if there is still
@@ -250,7 +257,8 @@
 
 QuicConsumedData QuicSpdyStream::WriteBodySlices(QuicMemSliceSpan slices,
                                                  bool fin) {
-  if (spdy_session_->connection()->transport_version() != QUIC_VERSION_99 ||
+  if (!VersionHasDataFrameHeader(
+          spdy_session_->connection()->transport_version()) ||
       slices.empty()) {
     return WriteMemSlices(slices, fin);
   }
@@ -264,23 +272,30 @@
 
   QuicConnection::ScopedPacketFlusher flusher(
       spdy_session_->connection(), QuicConnection::SEND_ACK_IF_PENDING);
+
+  // Write frame header.
   struct iovec header_iov = {static_cast<void*>(buffer.get()), header_length};
   QuicMemSliceStorage storage(
       &header_iov, 1,
       spdy_session_->connection()->helper()->GetStreamSendBufferAllocator(),
       GetQuicFlag(FLAGS_quic_send_buffer_max_data_slice_size));
-  WriteMemSlices(storage.ToSpan(), false);
+  unacked_frame_headers_offsets_.Add(
+      send_buffer().stream_offset(),
+      send_buffer().stream_offset() + header_length);
   QUIC_DLOG(INFO) << "Stream " << id() << " is writing header of length "
                   << header_length;
-  total_header_bytes_written_ += header_length;
-  QUIC_DLOG(INFO) << "Stream" << id() << " is writing body of length "
+  WriteMemSlices(storage.ToSpan(), false);
+
+  // Write body.
+  QUIC_DLOG(INFO) << "Stream " << id() << " is writing body of length "
                   << slices.total_length();
   return WriteMemSlices(slices, fin);
 }
 
 size_t QuicSpdyStream::Readv(const struct iovec* iov, size_t iov_len) {
   DCHECK(FinishedReadingHeaders());
-  if (spdy_session_->connection()->transport_version() != QUIC_VERSION_99) {
+  if (!VersionHasDataFrameHeader(
+          spdy_session_->connection()->transport_version())) {
     return sequencer()->Readv(iov, iov_len);
   }
   return body_buffer_.ReadBody(iov, iov_len);
@@ -288,7 +303,8 @@
 
 int QuicSpdyStream::GetReadableRegions(iovec* iov, size_t iov_len) const {
   DCHECK(FinishedReadingHeaders());
-  if (spdy_session_->connection()->transport_version() != QUIC_VERSION_99) {
+  if (!VersionHasDataFrameHeader(
+          spdy_session_->connection()->transport_version())) {
     return sequencer()->GetReadableRegions(iov, iov_len);
   }
   return body_buffer_.PeekBody(iov, iov_len);
@@ -296,8 +312,10 @@
 
 void QuicSpdyStream::MarkConsumed(size_t num_bytes) {
   DCHECK(FinishedReadingHeaders());
-  if (spdy_session_->connection()->transport_version() != QUIC_VERSION_99) {
-    return sequencer()->MarkConsumed(num_bytes);
+  if (!VersionHasDataFrameHeader(
+          spdy_session_->connection()->transport_version())) {
+    sequencer()->MarkConsumed(num_bytes);
+    return;
   }
   body_buffer_.MarkBodyConsumed(num_bytes);
 }
@@ -310,7 +328,8 @@
 }
 
 bool QuicSpdyStream::HasBytesToRead() const {
-  if (spdy_session_->connection()->transport_version() != QUIC_VERSION_99) {
+  if (!VersionHasDataFrameHeader(
+          spdy_session_->connection()->transport_version())) {
     return sequencer()->HasBytesToRead();
   }
   return body_buffer_.HasBytesToRead();
@@ -321,7 +340,8 @@
 }
 
 uint64_t QuicSpdyStream::total_body_bytes_read() const {
-  if (spdy_session_->connection()->transport_version() == QUIC_VERSION_99) {
+  if (VersionHasDataFrameHeader(
+          spdy_session_->connection()->transport_version())) {
     return body_buffer_.total_body_bytes_received();
   }
   return sequencer()->NumBytesConsumed();
@@ -424,6 +444,14 @@
       QuicStreamFrame(id(), fin, final_byte_offset, QuicStringPiece()));
 }
 
+size_t QuicSpdyStream::WriteHeadersImpl(
+    spdy::SpdyHeaderBlock header_block,
+    bool fin,
+    QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+  return spdy_session_->WriteHeadersOnHeadersStream(
+      id(), std::move(header_block), fin, priority(), std::move(ack_listener));
+}
+
 void QuicSpdyStream::OnPriorityFrame(SpdyPriority priority) {
   DCHECK_EQ(Perspective::IS_SERVER, session()->connection()->perspective());
   SetPriority(priority);
@@ -442,7 +470,8 @@
 }
 
 void QuicSpdyStream::OnDataAvailable() {
-  if (session()->connection()->transport_version() != QUIC_VERSION_99) {
+  if (!VersionHasDataFrameHeader(
+          session()->connection()->transport_version())) {
     OnBodyAvailable();
     return;
   }
@@ -543,5 +572,52 @@
            << body_buffer_.total_body_bytes_received();
 }
 
+bool QuicSpdyStream::OnStreamFrameAcked(QuicStreamOffset offset,
+                                        QuicByteCount data_length,
+                                        bool fin_acked,
+                                        QuicTime::Delta ack_delay_time,
+                                        QuicByteCount* newly_acked_length) {
+  const bool new_data_acked = QuicStream::OnStreamFrameAcked(
+      offset, data_length, fin_acked, ack_delay_time, newly_acked_length);
+
+  const QuicByteCount newly_acked_header_length =
+      GetNumFrameHeadersInInterval(offset, data_length);
+  DCHECK_LE(newly_acked_header_length, *newly_acked_length);
+  unacked_frame_headers_offsets_.Difference(offset, offset + data_length);
+  if (ack_listener_ != nullptr && new_data_acked) {
+    ack_listener_->OnPacketAcked(
+        *newly_acked_length - newly_acked_header_length, ack_delay_time);
+  }
+  return new_data_acked;
+}
+
+void QuicSpdyStream::OnStreamFrameRetransmitted(QuicStreamOffset offset,
+                                                QuicByteCount data_length,
+                                                bool fin_retransmitted) {
+  QuicStream::OnStreamFrameRetransmitted(offset, data_length,
+                                         fin_retransmitted);
+
+  const QuicByteCount retransmitted_header_length =
+      GetNumFrameHeadersInInterval(offset, data_length);
+  DCHECK_LE(retransmitted_header_length, data_length);
+
+  if (ack_listener_ != nullptr) {
+    ack_listener_->OnPacketRetransmitted(data_length -
+                                         retransmitted_header_length);
+  }
+}
+
+QuicByteCount QuicSpdyStream::GetNumFrameHeadersInInterval(
+    QuicStreamOffset offset,
+    QuicByteCount data_length) const {
+  QuicByteCount header_acked_length = 0;
+  QuicIntervalSet<QuicStreamOffset> newly_acked(offset, offset + data_length);
+  newly_acked.Intersection(unacked_frame_headers_offsets_);
+  for (const auto& interval : newly_acked) {
+    header_acked_length += interval.Length();
+  }
+  return header_acked_length;
+}
+
 #undef ENDPOINT  // undef for jumbo builds
 }  // namespace quic
diff --git a/quic/core/http/quic_spdy_stream.h b/quic/core/http/quic_spdy_stream.h
index eccec56..2bad21a 100644
--- a/quic/core/http/quic_spdy_stream.h
+++ b/quic/core/http/quic_spdy_stream.h
@@ -31,6 +31,7 @@
 namespace quic {
 
 namespace test {
+class QuicSpdyStreamPeer;
 class QuicStreamPeer;
 }  // namespace test
 
@@ -117,10 +118,7 @@
       QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
 
   // Sends |data| to the peer, or buffers if it can't be sent immediately.
-  void WriteOrBufferBody(
-      QuicStringPiece data,
-      bool fin,
-      QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+  void WriteOrBufferBody(QuicStringPiece data, bool fin);
 
   // Writes the trailers contained in |trailer_block| to the dedicated
   // headers stream. Trailers will always have the FIN set.
@@ -128,6 +126,18 @@
       spdy::SpdyHeaderBlock trailer_block,
       QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
 
+  // Override to report newly acked bytes via ack_listener_.
+  bool OnStreamFrameAcked(QuicStreamOffset offset,
+                          QuicByteCount data_length,
+                          bool fin_acked,
+                          QuicTime::Delta ack_delay_time,
+                          QuicByteCount* newly_acked_length) override;
+
+  // Override to report bytes retransmitted via ack_listener_.
+  void OnStreamFrameRetransmitted(QuicStreamOffset offset,
+                                  QuicByteCount data_length,
+                                  bool fin_retransmitted) override;
+
   // Does the same thing as WriteOrBufferBody except this method takes iovec
   // as the data input. Right now it only calls WritevData.
   // TODO(renjietang): Write data frame header before writing body.
@@ -166,10 +176,6 @@
 
   bool headers_decompressed() const { return headers_decompressed_; }
 
-  size_t total_header_bytes_written() const {
-    return total_header_bytes_written_;
-  }
-
   // Returns total amount of body bytes that have been read.
   uint64_t total_body_bytes_read() const;
 
@@ -210,16 +216,36 @@
   virtual void OnTrailingHeadersComplete(bool fin,
                                          size_t frame_len,
                                          const QuicHeaderList& header_list);
+  virtual size_t WriteHeadersImpl(
+      spdy::SpdyHeaderBlock header_block,
+      bool fin,
+      QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+
   QuicSpdySession* spdy_session() const { return spdy_session_; }
   Visitor* visitor() { return visitor_; }
 
   void set_headers_decompressed(bool val) { headers_decompressed_ = val; }
 
+  void set_ack_listener(
+      QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+    ack_listener_ = std::move(ack_listener);
+  }
+
+  const QuicIntervalSet<QuicStreamOffset>& unacked_frame_headers_offsets() {
+    return unacked_frame_headers_offsets_;
+  }
+
  private:
+  friend class test::QuicSpdyStreamPeer;
   friend class test::QuicStreamPeer;
   friend class QuicStreamUtils;
   class HttpDecoderVisitor;
 
+  // Given the interval marked by [|offset|, |offset| + |data_length|), return
+  // the number of frame header bytes contained in it.
+  QuicByteCount GetNumFrameHeadersInInterval(QuicStreamOffset offset,
+                                             QuicByteCount data_length) const;
+
   QuicSpdySession* spdy_session_;
 
   Visitor* visitor_;
@@ -244,8 +270,13 @@
   std::unique_ptr<HttpDecoderVisitor> http_decoder_visitor_;
   // Buffer that contains decoded data of the stream.
   QuicSpdyStreamBodyBuffer body_buffer_;
-  // Total bytes of header written to the stream.
-  size_t total_header_bytes_written_;
+
+  // Ack listener of this stream, and it is notified when any of written bytes
+  // are acked or retransmitted.
+  QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener_;
+
+  // Offset of unacked frame headers.
+  QuicIntervalSet<QuicStreamOffset> unacked_frame_headers_offsets_;
 };
 
 }  // namespace quic
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc
index 07bb9ed..1aabe0e 100644
--- a/quic/core/http/quic_spdy_stream_test.cc
+++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -12,6 +12,7 @@
 #include "net/third_party/quiche/src/quic/core/quic_connection.h"
 #include "net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h"
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
 #include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
@@ -24,6 +25,7 @@
 #include "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_stream_peer.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
 
@@ -32,7 +34,7 @@
 using spdy::SpdyHeaderBlock;
 using spdy::SpdyPriority;
 using testing::_;
-using testing::AnyNumber;
+using testing::AtLeast;
 using testing::Invoke;
 using testing::Return;
 using testing::StrictMock;
@@ -50,6 +52,11 @@
              bool should_process_data)
       : QuicSpdyStream(id, session, BIDIRECTIONAL),
         should_process_data_(should_process_data) {}
+  ~TestStream() override = default;
+
+  using QuicSpdyStream::set_ack_listener;
+  using QuicStream::CloseWriteSide;
+  using QuicStream::WriteOrBufferData;
 
   void OnBodyAvailable() override {
     if (!should_process_data_) {
@@ -63,14 +70,23 @@
     data_ += QuicString(buffer, bytes_read);
   }
 
-  using QuicSpdyStream::set_ack_listener;
-  using QuicStream::CloseWriteSide;
-  using QuicStream::WriteOrBufferData;
+  MOCK_METHOD1(WriteHeadersMock, void(bool fin));
+
+  size_t WriteHeadersImpl(spdy::SpdyHeaderBlock header_block,
+                          bool fin,
+                          QuicReferenceCountedPointer<QuicAckListenerInterface>
+                              ack_listener) override {
+    saved_headers_ = std::move(header_block);
+    WriteHeadersMock(fin);
+    return 0;
+  }
 
   const QuicString& data() const { return data_; }
+  const spdy::SpdyHeaderBlock& saved_headers() const { return saved_headers_; }
 
  private:
   bool should_process_data_;
+  spdy::SpdyHeaderBlock saved_headers_;
   QuicString data_;
 };
 
@@ -128,17 +144,21 @@
   }
 
   void Initialize(bool stream_should_process_data) {
-    connection_ = new testing::StrictMock<MockQuicConnection>(
+    connection_ = new StrictMock<MockQuicConnection>(
         &helper_, &alarm_factory_, Perspective::IS_SERVER,
         SupportedVersions(GetParam()));
-    session_ =
-        QuicMakeUnique<testing::StrictMock<MockQuicSpdySession>>(connection_);
+    session_ = QuicMakeUnique<StrictMock<MockQuicSpdySession>>(connection_);
     session_->Initialize();
-    stream_ = new TestStream(GetNthClientInitiatedBidirectionalId(0),
-                             session_.get(), stream_should_process_data);
+    ON_CALL(*session_, WritevData(_, _, _, _, _))
+        .WillByDefault(Invoke(MockQuicSession::ConsumeData));
+
+    stream_ =
+        new StrictMock<TestStream>(GetNthClientInitiatedBidirectionalId(0),
+                                   session_.get(), stream_should_process_data);
     session_->ActivateStream(QuicWrapUnique(stream_));
-    stream2_ = new TestStream(GetNthClientInitiatedBidirectionalId(1),
-                              session_.get(), stream_should_process_data);
+    stream2_ =
+        new StrictMock<TestStream>(GetNthClientInitiatedBidirectionalId(1),
+                                   session_.get(), stream_should_process_data);
     session_->ActivateStream(QuicWrapUnique(stream2_));
   }
 
@@ -149,8 +169,12 @@
   }
 
   QuicStreamId GetNthClientInitiatedBidirectionalId(int n) {
-    return QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
-        *session_, n);
+    return GetNthClientInitiatedBidirectionalStreamId(
+        connection_->transport_version(), n);
+  }
+
+  bool HasFrameHeader() const {
+    return VersionHasDataFrameHeader(connection_->transport_version());
   }
 
  protected:
@@ -168,9 +192,8 @@
   HttpEncoder encoder_;
 };
 
-INSTANTIATE_TEST_CASE_P(Tests,
-                        QuicSpdyStreamTest,
-                        ::testing::ValuesIn(AllSupportedVersions()));
+INSTANTIATE_TEST_SUITE_P(Tests, QuicSpdyStreamTest,
+                         ::testing::ValuesIn(AllSupportedVersions()));
 
 TEST_P(QuicSpdyStreamTest, ProcessHeaderList) {
   Initialize(kShouldProcessData);
@@ -288,9 +311,7 @@
   QuicByteCount header_length =
       encoder_.SerializeDataFrameHeader(body.length(), &buffer);
   QuicString header = QuicString(buffer.get(), header_length);
-  QuicString data = connection_->transport_version() == QUIC_VERSION_99
-                        ? header + body
-                        : body;
+  QuicString data = HasFrameHeader() ? header + body : body;
 
   EXPECT_EQ("", stream_->data());
   QuicHeaderList headers = ProcessHeaders(false, headers_);
@@ -310,9 +331,7 @@
   QuicByteCount header_length =
       encoder_.SerializeDataFrameHeader(body.length(), &buffer);
   QuicString header = QuicString(buffer.get(), header_length);
-  QuicString data = connection_->transport_version() == QUIC_VERSION_99
-                        ? header + body
-                        : body;
+  QuicString data = HasFrameHeader() ? header + body : body;
 
   for (size_t fragment_size = 1; fragment_size < data.size(); ++fragment_size) {
     Initialize(kShouldProcessData);
@@ -338,9 +357,7 @@
   QuicByteCount header_length =
       encoder_.SerializeDataFrameHeader(body.length(), &buffer);
   QuicString header = QuicString(buffer.get(), header_length);
-  QuicString data = connection_->transport_version() == QUIC_VERSION_99
-                        ? header + body
-                        : body;
+  QuicString data = HasFrameHeader() ? header + body : body;
 
   for (size_t split_point = 1; split_point < data.size() - 1; ++split_point) {
     Initialize(kShouldProcessData);
@@ -371,9 +388,7 @@
   QuicByteCount header_length =
       encoder_.SerializeDataFrameHeader(body.length(), &buf);
   QuicString header = QuicString(buf.get(), header_length);
-  QuicString data = connection_->transport_version() == QUIC_VERSION_99
-                        ? header + body
-                        : body;
+  QuicString data = HasFrameHeader() ? header + body : body;
 
   ProcessHeaders(false, headers_);
   QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0,
@@ -399,9 +414,7 @@
   QuicByteCount header_length =
       encoder_.SerializeDataFrameHeader(body.length(), &buf);
   QuicString header = QuicString(buf.get(), header_length);
-  QuicString data = connection_->transport_version() == QUIC_VERSION_99
-                        ? header + body
-                        : body;
+  QuicString data = HasFrameHeader() ? header + body : body;
   ProcessHeaders(false, headers_);
   QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0,
                         QuicStringPiece(data));
@@ -428,9 +441,7 @@
   QuicByteCount header_length =
       encoder_.SerializeDataFrameHeader(body.length(), &buf);
   QuicString header = QuicString(buf.get(), header_length);
-  QuicString data = connection_->transport_version() == QUIC_VERSION_99
-                        ? header + body
-                        : body;
+  QuicString data = HasFrameHeader() ? header + body : body;
 
   ProcessHeaders(false, headers_);
   QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0,
@@ -456,13 +467,9 @@
   QuicByteCount header_length =
       encoder_.SerializeDataFrameHeader(body1.length(), &buf);
   QuicString header = QuicString(buf.get(), header_length);
-  QuicString data1 = connection_->transport_version() == QUIC_VERSION_99
-                         ? header + body1
-                         : body1;
+  QuicString data1 = HasFrameHeader() ? header + body1 : body1;
   header_length = encoder_.SerializeDataFrameHeader(body2.length(), &buf);
-  QuicString data2 = connection_->transport_version() == QUIC_VERSION_99
-                         ? header + body2
-                         : body2;
+  QuicString data2 = HasFrameHeader() ? header + body2 : body2;
 
   ProcessHeaders(false, headers_);
   QuicStreamFrame frame1(GetNthClientInitiatedBidirectionalId(0), false, 0,
@@ -486,9 +493,7 @@
   QuicByteCount header_length =
       encoder_.SerializeDataFrameHeader(body.length(), &buf);
   QuicString header = QuicString(buf.get(), header_length);
-  QuicString data = connection_->transport_version() == QUIC_VERSION_99
-                        ? header + body
-                        : body;
+  QuicString data = HasFrameHeader() ? header + body : body;
 
   ProcessHeaders(false, headers_);
   QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0,
@@ -516,9 +521,7 @@
   QuicByteCount header_length =
       encoder_.SerializeDataFrameHeader(body.length(), &buf);
   QuicString header = QuicString(buf.get(), header_length);
-  QuicString data = connection_->transport_version() == QUIC_VERSION_99
-                        ? header + body
-                        : body;
+  QuicString data = HasFrameHeader() ? header + body : body;
 
   ProcessHeaders(false, headers_);
   QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0,
@@ -558,17 +561,15 @@
   // Try to send more data than the flow control limit allows.
   const uint64_t kOverflow = 15;
   QuicString body(kWindow + kOverflow, 'a');
-  bool is_version_99 = connection_->transport_version() == QUIC_VERSION_99;
 
-  const uint64_t kHeaderLength = is_version_99 ? 2 : 0;
-  if (is_version_99) {
-    EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
-        .WillOnce(Return(QuicConsumedData(2, false)));
+  const uint64_t kHeaderLength = HasFrameHeader() ? 2 : 0;
+  if (HasFrameHeader()) {
+    EXPECT_CALL(*session_, WritevData(_, _, kHeaderLength, _, NO_FIN));
   }
   EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
       .WillOnce(Return(QuicConsumedData(kWindow - kHeaderLength, true)));
   EXPECT_CALL(*connection_, SendControlFrame(_));
-  stream_->WriteOrBufferBody(body, false, nullptr);
+  stream_->WriteOrBufferBody(body, false);
 
   // Should have sent as much as possible, resulting in no send window left.
   EXPECT_EQ(0u,
@@ -605,7 +606,7 @@
   QuicByteCount header_length = 0;
   QuicString data;
 
-  if (connection_->transport_version() == QUIC_VERSION_99) {
+  if (HasFrameHeader()) {
     std::unique_ptr<char[]> buffer;
     header_length = encoder_.SerializeDataFrameHeader(body.length(), &buffer);
     QuicString header = QuicString(buffer.get(), header_length);
@@ -654,7 +655,7 @@
   QuicByteCount header_length = 0;
   QuicString data;
 
-  if (connection_->transport_version() == QUIC_VERSION_99) {
+  if (HasFrameHeader()) {
     std::unique_ptr<char[]> buffer;
     header_length = encoder_.SerializeDataFrameHeader(body.length(), &buffer);
     QuicString header = QuicString(buffer.get(), header_length);
@@ -723,7 +724,7 @@
   QuicString data2;
   QuicString body2(1, 'a');
 
-  if (connection_->transport_version() == QUIC_VERSION_99) {
+  if (HasFrameHeader()) {
     body = QuicString(kWindow / 4 - 2, 'a');
     std::unique_ptr<char[]> buffer;
     header_length = encoder_.SerializeDataFrameHeader(body.length(), &buffer);
@@ -777,9 +778,7 @@
   QuicByteCount header_length =
       encoder_.SerializeDataFrameHeader(body.length(), &buf);
   QuicString header = QuicString(buf.get(), header_length);
-  QuicString data = connection_->transport_version() == QUIC_VERSION_99
-                        ? header + body
-                        : body;
+  QuicString data = HasFrameHeader() ? header + body : body;
   QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0,
                         QuicStringPiece(data));
   EXPECT_CALL(*connection_,
@@ -822,9 +821,7 @@
   QuicByteCount header_length =
       encoder_.SerializeDataFrameHeader(body.length(), &buf);
   QuicString header = QuicString(buf.get(), header_length);
-  QuicString data = connection_->transport_version() == QUIC_VERSION_99
-                        ? header + body
-                        : body;
+  QuicString data = HasFrameHeader() ? header + body : body;
 
   EXPECT_LT(data.size(), kStreamWindow);
   QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0,
@@ -853,10 +850,9 @@
   EXPECT_CALL(*connection_,
               SendBlocked(GetNthClientInitiatedBidirectionalId(0)))
       .Times(0);
-  EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
-      .WillOnce(Return(QuicConsumedData(0, fin)));
+  EXPECT_CALL(*session_, WritevData(_, _, 0, _, FIN));
 
-  stream_->WriteOrBufferBody(body, fin, nullptr);
+  stream_->WriteOrBufferBody(body, fin);
 }
 
 TEST_P(QuicSpdyStreamTest, ReceivingTrailersViaHeaderList) {
@@ -915,9 +911,7 @@
   QuicByteCount header_length =
       encoder_.SerializeDataFrameHeader(body.length(), &buf);
   QuicString header = QuicString(buf.get(), header_length);
-  QuicString data = connection_->transport_version() == QUIC_VERSION_99
-                        ? header + body
-                        : body;
+  QuicString data = HasFrameHeader() ? header + body : body;
 
   // Receive trailing headers.
   SpdyHeaderBlock trailers_block;
@@ -1052,9 +1046,7 @@
   QuicByteCount header_length =
       encoder_.SerializeDataFrameHeader(body.length(), &buf);
   QuicString header = QuicString(buf.get(), header_length);
-  QuicString data = connection_->transport_version() == QUIC_VERSION_99
-                        ? header + body
-                        : body;
+  QuicString data = HasFrameHeader() ? header + body : body;
 
   QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), /*fin=*/true,
                         0, data);
@@ -1067,18 +1059,15 @@
   // Test that writing trailers will send a FIN, as Trailers are the last thing
   // to be sent on a stream.
   Initialize(kShouldProcessData);
-  EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
-      .Times(AnyNumber())
-      .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
 
   // Write the initial headers, without a FIN.
-  EXPECT_CALL(*session_, WriteHeadersMock(_, _, _, _, _));
+  EXPECT_CALL(*stream_, WriteHeadersMock(false));
   stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/false, nullptr);
 
   // Writing trailers implicitly sends a FIN.
   SpdyHeaderBlock trailers;
   trailers["trailer key"] = "trailer value";
-  EXPECT_CALL(*session_, WriteHeadersMock(_, _, true, _, _));
+  EXPECT_CALL(*stream_, WriteHeadersMock(true));
   stream_->WriteTrailers(std::move(trailers), nullptr);
   EXPECT_TRUE(stream_->fin_sent());
 }
@@ -1087,23 +1076,21 @@
   // Test that when writing trailers, the trailers that are actually sent to the
   // peer contain the final offset field indicating last byte of data.
   Initialize(kShouldProcessData);
-  EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
-      .Times(AnyNumber())
-      .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
 
   // Write the initial headers.
-  EXPECT_CALL(*session_, WriteHeadersMock(_, _, _, _, _));
+  EXPECT_CALL(*stream_, WriteHeadersMock(false));
   stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/false, nullptr);
 
   // Write non-zero body data to force a non-zero final offset.
-  QuicString body(1024, 'x');  // 1 MB
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1));
+  QuicString body(1024, 'x');  // 1 kB
   QuicByteCount header_length = 0;
-  if (connection_->transport_version() == QUIC_VERSION_99) {
+  if (HasFrameHeader()) {
     std::unique_ptr<char[]> buf;
     header_length = encoder_.SerializeDataFrameHeader(body.length(), &buf);
   }
 
-  stream_->WriteOrBufferBody(body, false, nullptr);
+  stream_->WriteOrBufferBody(body, false);
 
   // The final offset field in the trailing headers is populated with the
   // number of body bytes written (including queued bytes).
@@ -1112,31 +1099,29 @@
   SpdyHeaderBlock trailers_with_offset(trailers.Clone());
   trailers_with_offset[kFinalOffsetHeaderKey] =
       QuicTextUtils::Uint64ToString(body.length() + header_length);
-  EXPECT_CALL(*session_, WriteHeadersMock(_, _, true, _, _));
+  EXPECT_CALL(*stream_, WriteHeadersMock(true));
   stream_->WriteTrailers(std::move(trailers), nullptr);
-  EXPECT_EQ(trailers_with_offset, session_->GetWriteHeaders());
+  EXPECT_EQ(trailers_with_offset, stream_->saved_headers());
 }
 
 TEST_P(QuicSpdyStreamTest, WritingTrailersClosesWriteSide) {
   // Test that if trailers are written after all other data has been written
   // (headers and body), that this closes the stream for writing.
   Initialize(kShouldProcessData);
-  EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
-      .Times(AnyNumber())
-      .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
 
   // Write the initial headers.
-  EXPECT_CALL(*session_, WriteHeadersMock(_, _, _, _, _));
+  EXPECT_CALL(*stream_, WriteHeadersMock(false));
   stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/false, nullptr);
 
   // Write non-zero body data.
-  const int kBodySize = 1 * 1024;  // 1 MB
-  stream_->WriteOrBufferBody(QuicString(kBodySize, 'x'), false, nullptr);
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1));
+  const int kBodySize = 1 * 1024;  // 1 kB
+  stream_->WriteOrBufferBody(QuicString(kBodySize, 'x'), false);
   EXPECT_EQ(0u, stream_->BufferedDataBytes());
 
   // Headers and body have been fully written, there is no queued data. Writing
   // trailers marks the end of this stream, and thus the write side is closed.
-  EXPECT_CALL(*session_, WriteHeadersMock(_, _, true, _, _));
+  EXPECT_CALL(*stream_, WriteHeadersMock(true));
   stream_->WriteTrailers(SpdyHeaderBlock(), nullptr);
   EXPECT_TRUE(stream_->write_side_closed());
 }
@@ -1146,35 +1131,30 @@
   // while there are still body bytes queued.
   testing::InSequence seq;
   Initialize(kShouldProcessData);
-  EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
-      .Times(AnyNumber())
-      .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
 
   // Write the initial headers.
-  EXPECT_CALL(*session_, WriteHeadersMock(_, _, _, _, _));
+  EXPECT_CALL(*stream_, WriteHeadersMock(false));
   stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/false, nullptr);
 
   // Write non-zero body data, but only consume partially, ensuring queueing.
-  const int kBodySize = 1 * 1024;  // 1 KB
-  if (connection_->transport_version() == QUIC_VERSION_99) {
-    EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
-        .WillOnce(Return(QuicConsumedData(3, false)));
+  const int kBodySize = 1 * 1024;  // 1 kB
+  if (HasFrameHeader()) {
+    EXPECT_CALL(*session_, WritevData(_, _, 3, _, NO_FIN));
   }
-  EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+  EXPECT_CALL(*session_, WritevData(_, _, kBodySize, _, NO_FIN))
       .WillOnce(Return(QuicConsumedData(kBodySize - 1, false)));
-  stream_->WriteOrBufferBody(QuicString(kBodySize, 'x'), false, nullptr);
+  stream_->WriteOrBufferBody(QuicString(kBodySize, 'x'), false);
   EXPECT_EQ(1u, stream_->BufferedDataBytes());
 
   // Writing trailers will send a FIN, but not close the write side of the
   // stream as there are queued bytes.
-  EXPECT_CALL(*session_, WriteHeadersMock(_, _, true, _, _));
+  EXPECT_CALL(*stream_, WriteHeadersMock(true));
   stream_->WriteTrailers(SpdyHeaderBlock(), nullptr);
   EXPECT_TRUE(stream_->fin_sent());
   EXPECT_FALSE(stream_->write_side_closed());
 
   // Writing the queued bytes will close the write side of the stream.
-  EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
-      .WillOnce(Return(QuicConsumedData(1, false)));
+  EXPECT_CALL(*session_, WritevData(_, _, 1, _, NO_FIN));
   stream_->OnCanWrite();
   EXPECT_TRUE(stream_->write_side_closed());
 }
@@ -1187,12 +1167,9 @@
 
   // Test that it is not possible to write Trailers after a FIN has been sent.
   Initialize(kShouldProcessData);
-  EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
-      .Times(AnyNumber())
-      .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
 
   // Write the initial headers, with a FIN.
-  EXPECT_CALL(*session_, WriteHeadersMock(_, _, _, _, _));
+  EXPECT_CALL(*stream_, WriteHeadersMock(true));
   stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/true, nullptr);
   EXPECT_TRUE(stream_->fin_sent());
 
@@ -1204,9 +1181,7 @@
 
 TEST_P(QuicSpdyStreamTest, HeaderStreamNotiferCorrespondingSpdyStream) {
   Initialize(kShouldProcessData);
-  EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
-      .Times(AnyNumber())
-      .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1));
   testing::InSequence s;
   QuicReferenceCountedPointer<MockAckListener> ack_listener1(
       new MockAckListener());
@@ -1217,20 +1192,26 @@
 
   session_->headers_stream()->WriteOrBufferData("Header1", false,
                                                 ack_listener1);
-  stream_->WriteOrBufferBody("Test1", true, nullptr);
+  stream_->WriteOrBufferBody("Test1", true);
 
   session_->headers_stream()->WriteOrBufferData("Header2", false,
                                                 ack_listener2);
-  stream2_->WriteOrBufferBody("Test2", false, nullptr);
+  stream2_->WriteOrBufferBody("Test2", false);
 
   QuicStreamFrame frame1(
       QuicUtils::GetHeadersStreamId(connection_->transport_version()), false, 0,
       "Header1");
-  QuicStreamFrame frame2(stream_->id(), true, 0, "Test1");
+  QuicString header = "";
+  if (HasFrameHeader()) {
+    std::unique_ptr<char[]> buffer;
+    QuicByteCount header_length = encoder_.SerializeDataFrameHeader(5, &buffer);
+    header = QuicString(buffer.get(), header_length);
+  }
+  QuicStreamFrame frame2(stream_->id(), true, 0, header + "Test1");
   QuicStreamFrame frame3(
       QuicUtils::GetHeadersStreamId(connection_->transport_version()), false, 7,
       "Header2");
-  QuicStreamFrame frame4(stream2_->id(), false, 0, "Test2");
+  QuicStreamFrame frame4(stream2_->id(), false, 0, header + "Test2");
 
   EXPECT_CALL(*ack_listener1, OnPacketRetransmitted(7));
   session_->OnStreamFrameRetransmitted(frame1);
@@ -1251,12 +1232,10 @@
 
 TEST_P(QuicSpdyStreamTest, StreamBecomesZombieWithWriteThatCloses) {
   Initialize(kShouldProcessData);
-  EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
-      .Times(AnyNumber())
-      .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1));
   QuicStreamPeer::CloseReadSide(stream_);
   // This write causes stream to be closed.
-  stream_->WriteOrBufferBody("Test1", true, nullptr);
+  stream_->WriteOrBufferBody("Test1", true);
   // stream_ has unacked data and should become zombie.
   EXPECT_TRUE(QuicContainsKey(QuicSessionPeer::zombie_streams(session_.get()),
                               stream_->id()));
@@ -1273,26 +1252,24 @@
   testing::InSequence seq;
   Initialize(kShouldProcessData);
 
-  if (connection_->transport_version() == QUIC_VERSION_99) {
-    EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
-        .WillOnce(Return(QuicConsumedData(2, false)));
+  if (HasFrameHeader()) {
+    EXPECT_CALL(*session_, WritevData(_, _, 2, _, NO_FIN));
   }
-  EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
-      .WillOnce(Return(QuicConsumedData(4, true)));
-  stream_->WriteOrBufferBody("data", true, nullptr);
+  EXPECT_CALL(*session_, WritevData(_, _, 4, _, FIN));
+  stream_->WriteOrBufferBody("data", true);
   stream_->OnPriorityFrame(kV3HighestPriority);
   EXPECT_EQ(kV3HighestPriority, stream_->priority());
 }
 
 TEST_P(QuicSpdyStreamTest, SetPriorityBeforeUpdateStreamPriority) {
-  MockQuicConnection* connection = new testing::StrictMock<MockQuicConnection>(
+  MockQuicConnection* connection = new StrictMock<MockQuicConnection>(
       &helper_, &alarm_factory_, Perspective::IS_SERVER,
       SupportedVersions(GetParam()));
   std::unique_ptr<TestMockUpdateStreamSession> session(
-      new testing::StrictMock<TestMockUpdateStreamSession>(connection));
-  TestStream* stream = new TestStream(
-      QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(*session,
-                                                                      0),
+      new StrictMock<TestMockUpdateStreamSession>(connection));
+  auto stream = new StrictMock<TestStream>(
+      GetNthClientInitiatedBidirectionalStreamId(
+          session->connection()->transport_version(), 0),
       session.get(),
       /*should_process_data=*/true);
   session->ActivateStream(QuicWrapUnique(stream));
@@ -1309,6 +1286,233 @@
   stream->SetPriority(kV3LowestPriority);
 }
 
+TEST_P(QuicSpdyStreamTest, StreamWaitsForAcks) {
+  Initialize(kShouldProcessData);
+  QuicReferenceCountedPointer<MockAckListener> mock_ack_listener(
+      new StrictMock<MockAckListener>);
+  stream_->set_ack_listener(mock_ack_listener);
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1));
+  // Stream is not waiting for acks initially.
+  EXPECT_FALSE(stream_->IsWaitingForAcks());
+  EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+
+  // Send kData1.
+  stream_->WriteOrBufferData("FooAndBar", false, nullptr);
+  EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
+  EXPECT_TRUE(stream_->IsWaitingForAcks());
+  EXPECT_CALL(*mock_ack_listener, OnPacketAcked(9, _));
+  QuicByteCount newly_acked_length = 0;
+  EXPECT_TRUE(stream_->OnStreamFrameAcked(0, 9, false, QuicTime::Delta::Zero(),
+                                          &newly_acked_length));
+  // Stream is not waiting for acks as all sent data is acked.
+  EXPECT_FALSE(stream_->IsWaitingForAcks());
+  EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+
+  // Send kData2.
+  stream_->WriteOrBufferData("FooAndBar", false, nullptr);
+  EXPECT_TRUE(stream_->IsWaitingForAcks());
+  EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
+  // Send FIN.
+  stream_->WriteOrBufferData("", true, nullptr);
+  // Fin only frame is not stored in send buffer.
+  EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
+
+  // kData2 is retransmitted.
+  EXPECT_CALL(*mock_ack_listener, OnPacketRetransmitted(9));
+  stream_->OnStreamFrameRetransmitted(9, 9, false);
+
+  // kData2 is acked.
+  EXPECT_CALL(*mock_ack_listener, OnPacketAcked(9, _));
+  EXPECT_TRUE(stream_->OnStreamFrameAcked(9, 9, false, QuicTime::Delta::Zero(),
+                                          &newly_acked_length));
+  // Stream is waiting for acks as FIN is not acked.
+  EXPECT_TRUE(stream_->IsWaitingForAcks());
+  EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+
+  // FIN is acked.
+  EXPECT_CALL(*mock_ack_listener, OnPacketAcked(0, _));
+  EXPECT_TRUE(stream_->OnStreamFrameAcked(18, 0, true, QuicTime::Delta::Zero(),
+                                          &newly_acked_length));
+  EXPECT_FALSE(stream_->IsWaitingForAcks());
+  EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+}
+
+TEST_P(QuicSpdyStreamTest, StreamDataGetAckedMultipleTimes) {
+  Initialize(kShouldProcessData);
+  QuicReferenceCountedPointer<MockAckListener> mock_ack_listener(
+      new StrictMock<MockAckListener>);
+  stream_->set_ack_listener(mock_ack_listener);
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1));
+  // Send [0, 27) and fin.
+  stream_->WriteOrBufferData("FooAndBar", false, nullptr);
+  stream_->WriteOrBufferData("FooAndBar", false, nullptr);
+  stream_->WriteOrBufferData("FooAndBar", true, nullptr);
+
+  // Ack [0, 9), [5, 22) and [18, 26)
+  // Verify [0, 9) 9 bytes are acked.
+  QuicByteCount newly_acked_length = 0;
+  EXPECT_CALL(*mock_ack_listener, OnPacketAcked(9, _));
+  EXPECT_TRUE(stream_->OnStreamFrameAcked(0, 9, false, QuicTime::Delta::Zero(),
+                                          &newly_acked_length));
+  EXPECT_EQ(2u, QuicStreamPeer::SendBuffer(stream_).size());
+  // Verify [9, 22) 13 bytes are acked.
+  EXPECT_CALL(*mock_ack_listener, OnPacketAcked(13, _));
+  EXPECT_TRUE(stream_->OnStreamFrameAcked(5, 17, false, QuicTime::Delta::Zero(),
+                                          &newly_acked_length));
+  EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
+  // Verify [22, 26) 4 bytes are acked.
+  EXPECT_CALL(*mock_ack_listener, OnPacketAcked(4, _));
+  EXPECT_TRUE(stream_->OnStreamFrameAcked(18, 8, false, QuicTime::Delta::Zero(),
+                                          &newly_acked_length));
+  EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
+  EXPECT_TRUE(stream_->IsWaitingForAcks());
+
+  // Ack [0, 27).
+  // Verify [26, 27) 1 byte is acked.
+  EXPECT_CALL(*mock_ack_listener, OnPacketAcked(1, _));
+  EXPECT_TRUE(stream_->OnStreamFrameAcked(26, 1, false, QuicTime::Delta::Zero(),
+                                          &newly_acked_length));
+  EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+  EXPECT_TRUE(stream_->IsWaitingForAcks());
+
+  // Ack Fin. Verify OnPacketAcked is called.
+  EXPECT_CALL(*mock_ack_listener, OnPacketAcked(0, _));
+  EXPECT_TRUE(stream_->OnStreamFrameAcked(27, 0, true, QuicTime::Delta::Zero(),
+                                          &newly_acked_length));
+  EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+  EXPECT_FALSE(stream_->IsWaitingForAcks());
+
+  // Ack [10, 27) and fin.
+  // No new data is acked, verify OnPacketAcked is not called.
+  EXPECT_CALL(*mock_ack_listener, OnPacketAcked(_, _)).Times(0);
+  EXPECT_FALSE(stream_->OnStreamFrameAcked(
+      10, 17, true, QuicTime::Delta::Zero(), &newly_acked_length));
+  EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+  EXPECT_FALSE(stream_->IsWaitingForAcks());
+}
+
+// HTTP/3 only.
+TEST_P(QuicSpdyStreamTest, HeadersAckNotReportedWriteOrBufferBody) {
+  Initialize(kShouldProcessData);
+  if (!HasFrameHeader()) {
+    return;
+  }
+  QuicReferenceCountedPointer<MockAckListener> mock_ack_listener(
+      new StrictMock<MockAckListener>);
+  stream_->set_ack_listener(mock_ack_listener);
+  QuicString body = "Test1";
+  QuicString body2(100, 'x');
+
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1));
+  stream_->WriteOrBufferBody(body, false);
+  stream_->WriteOrBufferBody(body2, true);
+
+  std::unique_ptr<char[]> buffer;
+  QuicByteCount header_length =
+      encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+  QuicString header = QuicString(buffer.get(), header_length);
+
+  header_length = encoder_.SerializeDataFrameHeader(body2.length(), &buffer);
+  QuicString header2 = QuicString(buffer.get(), header_length);
+
+  EXPECT_CALL(*mock_ack_listener, OnPacketAcked(body.length(), _));
+  QuicStreamFrame frame(stream_->id(), false, 0, header + body);
+  EXPECT_TRUE(
+      session_->OnFrameAcked(QuicFrame(frame), QuicTime::Delta::Zero()));
+
+  EXPECT_CALL(*mock_ack_listener, OnPacketAcked(0, _));
+  QuicStreamFrame frame2(stream_->id(), false, (header + body).length(),
+                         header2);
+  EXPECT_TRUE(
+      session_->OnFrameAcked(QuicFrame(frame2), QuicTime::Delta::Zero()));
+
+  EXPECT_CALL(*mock_ack_listener, OnPacketAcked(body2.length(), _));
+  QuicStreamFrame frame3(stream_->id(), true,
+                         (header + body).length() + header2.length(), body2);
+  EXPECT_TRUE(
+      session_->OnFrameAcked(QuicFrame(frame3), QuicTime::Delta::Zero()));
+
+  EXPECT_TRUE(
+      QuicSpdyStreamPeer::unacked_frame_headers_offsets(stream_).Empty());
+}
+
+// HTTP/3 only.
+TEST_P(QuicSpdyStreamTest, HeadersAckNotReportedWriteBodySlices) {
+  Initialize(kShouldProcessData);
+  if (!HasFrameHeader()) {
+    return;
+  }
+  QuicReferenceCountedPointer<MockAckListener> mock_ack_listener(
+      new StrictMock<MockAckListener>);
+  stream_->set_ack_listener(mock_ack_listener);
+  QuicString body = "Test1";
+  QuicString body2(100, 'x');
+  struct iovec body1_iov = {const_cast<char*>(body.data()), body.length()};
+  struct iovec body2_iov = {const_cast<char*>(body2.data()), body2.length()};
+  QuicMemSliceStorage storage(&body1_iov, 1,
+                              helper_.GetStreamSendBufferAllocator(), 1024);
+  QuicMemSliceStorage storage2(&body2_iov, 1,
+                               helper_.GetStreamSendBufferAllocator(), 1024);
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1));
+  stream_->WriteBodySlices(storage.ToSpan(), false);
+  stream_->WriteBodySlices(storage2.ToSpan(), true);
+
+  std::unique_ptr<char[]> buffer;
+  QuicByteCount header_length =
+      encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+  QuicString header = QuicString(buffer.get(), header_length);
+
+  header_length = encoder_.SerializeDataFrameHeader(body2.length(), &buffer);
+  QuicString header2 = QuicString(buffer.get(), header_length);
+
+  EXPECT_CALL(*mock_ack_listener,
+              OnPacketAcked(body.length() + body2.length(), _));
+  QuicStreamFrame frame(stream_->id(), true, 0,
+                        header + body + header2 + body2);
+  EXPECT_TRUE(
+      session_->OnFrameAcked(QuicFrame(frame), QuicTime::Delta::Zero()));
+
+  EXPECT_TRUE(
+      QuicSpdyStreamPeer::unacked_frame_headers_offsets(stream_).Empty());
+}
+
+// HTTP/3 only.
+TEST_P(QuicSpdyStreamTest, HeaderBytesNotReportedOnRetransmission) {
+  Initialize(kShouldProcessData);
+  if (!HasFrameHeader()) {
+    return;
+  }
+  QuicReferenceCountedPointer<MockAckListener> mock_ack_listener(
+      new StrictMock<MockAckListener>);
+  stream_->set_ack_listener(mock_ack_listener);
+  QuicString body = "Test1";
+  QuicString body2(100, 'x');
+
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1));
+  stream_->WriteOrBufferBody(body, false);
+  stream_->WriteOrBufferBody(body2, true);
+
+  std::unique_ptr<char[]> buffer;
+  QuicByteCount header_length =
+      encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+  QuicString header = QuicString(buffer.get(), header_length);
+
+  header_length = encoder_.SerializeDataFrameHeader(body2.length(), &buffer);
+  QuicString header2 = QuicString(buffer.get(), header_length);
+
+  EXPECT_CALL(*mock_ack_listener, OnPacketRetransmitted(body.length()));
+  QuicStreamFrame frame(stream_->id(), false, 0, header + body);
+  session_->OnStreamFrameRetransmitted(frame);
+
+  EXPECT_CALL(*mock_ack_listener, OnPacketRetransmitted(body2.length()));
+  QuicStreamFrame frame2(stream_->id(), true, (header + body).length(),
+                         header2 + body2);
+  session_->OnStreamFrameRetransmitted(frame2);
+
+  EXPECT_FALSE(
+      QuicSpdyStreamPeer::unacked_frame_headers_offsets(stream_).Empty());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/http/spdy_utils.cc b/quic/core/http/spdy_utils.cc
index d8fc79d..aa6d6e7 100644
--- a/quic/core/http/spdy_utils.cc
+++ b/quic/core/http/spdy_utils.cc
@@ -255,12 +255,12 @@
   // Validate the scheme; this is to ensure a scheme of "foo://bar" is not
   // parsed as a URL of "foo://bar://baz" when combined with a host of "baz".
   std::string canonical_scheme;
-  url::StdStringCanonOutput canon_output(&canonical_scheme);
+  url::StdStringCanonOutput canon_scheme_output(&canonical_scheme);
   url::Component canon_component;
   url::Component scheme_component(0, scheme.size());
 
-  if (!url::CanonicalizeScheme(scheme.data(), scheme_component, &canon_output,
-                               &canon_component) ||
+  if (!url::CanonicalizeScheme(scheme.data(), scheme_component,
+                               &canon_scheme_output, &canon_component) ||
       !canon_component.is_nonempty() || canon_component.begin != 0) {
     return QuicString();
   }
@@ -304,13 +304,13 @@
     }
   }
 
-  // Validate the host by attempting to canoncalize it. Invalid characters
+  // Validate the host by attempting to canonicalize it. Invalid characters
   // will result in a canonicalization failure (e.g. '/')
   std::string canon_host;
-  canon_output = url::StdStringCanonOutput(&canon_host);
+  url::StdStringCanonOutput canon_host_output(&canon_host);
   canon_component.reset();
-  if (!url::CanonicalizeHost(authority.data(), host_component, &canon_output,
-                             &canon_component) ||
+  if (!url::CanonicalizeHost(authority.data(), host_component,
+                             &canon_host_output, &canon_component) ||
       !canon_component.is_nonempty() || canon_component.begin != 0) {
     return QuicString();
   }
diff --git a/quic/core/qpack/offline/qpack_offline_decoder_bin.cc b/quic/core/qpack/offline/qpack_offline_decoder_bin.cc
index 499c9fd..9d2be7c 100644
--- a/quic/core/qpack/offline/qpack_offline_decoder_bin.cc
+++ b/quic/core/qpack/offline/qpack_offline_decoder_bin.cc
@@ -12,18 +12,21 @@
 #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
 
 int main(int argc, char* argv[]) {
-  InitGoogle(argv[0], &argc, &argv, false);
+  const char* usage =
+      "Usage: qpack_offline_decoder input_filename expected_headers_filename "
+      "....";
+  std::vector<quic::QuicString> args =
+      quic::QuicParseCommandLineFlags(usage, argc, argv);
 
-  if (argc < 3 || argc % 2 != 1) {
-    QUIC_LOG(ERROR) << "Usage: " << argv[0]
-                    << " input_filename expected_headers_filename ...";
+  if (args.size() < 2 || args.size() % 2 != 0) {
+    quic::QuicPrintCommandLineFlagHelp(usage);
     return 1;
   }
 
-  int i;
-  for (i = 0; 2 * i + 1 < argc; ++i) {
-    const quic::QuicStringPiece input_filename(argv[2 * i + 1]);
-    const quic::QuicStringPiece expected_headers_filename(argv[2 * i + 2]);
+  size_t i;
+  for (i = 0; 2 * i < args.size(); ++i) {
+    const quic::QuicStringPiece input_filename(args[2 * i]);
+    const quic::QuicStringPiece expected_headers_filename(args[2 * i + 1]);
 
     // Every file represents a different connection,
     // therefore every file needs a fresh decoding context.
diff --git a/quic/core/qpack/qpack_decoded_headers_accumulator.cc b/quic/core/qpack/qpack_decoded_headers_accumulator.cc
new file mode 100644
index 0000000..ac60fce
--- /dev/null
+++ b/quic/core/qpack/qpack_decoded_headers_accumulator.cc
@@ -0,0 +1,70 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h"
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h"
+
+namespace quic {
+
+QpackDecodedHeadersAccumulator::QpackDecodedHeadersAccumulator(
+    QuicStreamId id,
+    QpackDecoder* qpack_decoder)
+    : decoder_(qpack_decoder->DecodeHeaderBlock(id, this)),
+      uncompressed_header_bytes_(0),
+      compressed_header_bytes_(0),
+      error_detected_(false) {
+  quic_header_list_.OnHeaderBlockStart();
+}
+
+void QpackDecodedHeadersAccumulator::OnHeaderDecoded(QuicStringPiece name,
+                                                     QuicStringPiece value) {
+  DCHECK(!error_detected_);
+
+  uncompressed_header_bytes_ += name.size() + value.size();
+  quic_header_list_.OnHeader(name, value);
+}
+
+void QpackDecodedHeadersAccumulator::OnDecodingCompleted() {}
+
+void QpackDecodedHeadersAccumulator::OnDecodingErrorDetected(
+    QuicStringPiece error_message) {
+  DCHECK(!error_detected_);
+
+  error_detected_ = true;
+  // Copy error message to ensure it remains valid for the lifetime of |this|.
+  error_message_.assign(error_message.data(), error_message.size());
+}
+
+bool QpackDecodedHeadersAccumulator::Decode(QuicStringPiece data) {
+  DCHECK(!error_detected_);
+
+  compressed_header_bytes_ += data.size();
+  decoder_->Decode(data);
+
+  return !error_detected_;
+}
+
+bool QpackDecodedHeadersAccumulator::EndHeaderBlock() {
+  DCHECK(!error_detected_);
+
+  decoder_->EndHeaderBlock();
+
+  quic_header_list_.OnHeaderBlockEnd(uncompressed_header_bytes_,
+                                     compressed_header_bytes_);
+
+  return !error_detected_;
+}
+
+const QuicHeaderList& QpackDecodedHeadersAccumulator::quic_header_list() const {
+  DCHECK(!error_detected_);
+  return quic_header_list_;
+}
+
+QuicStringPiece QpackDecodedHeadersAccumulator::error_message() const {
+  DCHECK(error_detected_);
+  return error_message_;
+}
+
+}  // namespace quic
diff --git a/quic/core/qpack/qpack_decoded_headers_accumulator.h b/quic/core/qpack/qpack_decoded_headers_accumulator.h
new file mode 100644
index 0000000..47d4f63
--- /dev/null
+++ b/quic/core/qpack/qpack_decoded_headers_accumulator.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QPACK_QPACK_DECODED_HEADERS_ACCUMULATOR_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_DECODED_HEADERS_ACCUMULATOR_H_
+
+#include <cstddef>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_header_list.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QpackDecoder;
+
+// A class that creates and owns a QpackProgressiveDecoder instance, accumulates
+// decoded headers in a QuicHeaderList, and keeps track of uncompressed and
+// compressed size so that it can be passed to QuicHeaderList::EndHeaderBlock().
+class QUIC_EXPORT_PRIVATE QpackDecodedHeadersAccumulator
+    : public QpackProgressiveDecoder::HeadersHandlerInterface {
+ public:
+  QpackDecodedHeadersAccumulator(QuicStreamId id, QpackDecoder* qpack_decoder);
+  virtual ~QpackDecodedHeadersAccumulator() = default;
+
+  // QpackProgressiveDecoder::HeadersHandlerInterface implementation.
+  // These methods should only be called by |decoder_|.
+  void OnHeaderDecoded(QuicStringPiece name, QuicStringPiece value) override;
+  void OnDecodingCompleted() override;
+  void OnDecodingErrorDetected(QuicStringPiece error_message) override;
+
+  // Decode payload data.  Returns true on success, false on error.
+  // Must not be called if an error has been detected.
+  // Must not be called after EndHeaderBlock().
+  bool Decode(QuicStringPiece data);
+
+  // Signal end of HEADERS frame.  Returns true on success, false on error.
+  // Must not be called if an error has been detected.
+  // Must not be called more that once.
+  bool EndHeaderBlock();
+
+  // Returns accumulated header list.
+  const QuicHeaderList& quic_header_list() const;
+
+  // Returns error message.
+  // Must not be called unless an error has been detected.
+  QuicStringPiece error_message() const;
+
+ private:
+  std::unique_ptr<QpackProgressiveDecoder> decoder_;
+  QuicHeaderList quic_header_list_;
+  size_t uncompressed_header_bytes_;
+  size_t compressed_header_bytes_;
+  bool error_detected_;
+  QuicString error_message_;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_CORE_QPACK_QPACK_DECODED_HEADERS_ACCUMULATOR_H_
diff --git a/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc b/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc
new file mode 100644
index 0000000..0c71b19
--- /dev/null
+++ b/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc
@@ -0,0 +1,100 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h"
+
+#include <cstring>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using ::testing::Eq;
+using ::testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+QuicStreamId kTestStreamId = 1;
+
+// Header Acknowledgement decoder stream instruction with stream_id = 1.
+const char* const kHeaderAcknowledgement = "\x81";
+
+}  // anonymous namespace
+
+class QpackDecodedHeadersAccumulatorTest : public QuicTest {
+ protected:
+  QpackDecodedHeadersAccumulatorTest()
+      : qpack_decoder_(&encoder_stream_error_delegate_,
+                       &decoder_stream_sender_delegate_),
+        accumulator_(kTestStreamId, &qpack_decoder_) {}
+
+  NoopEncoderStreamErrorDelegate encoder_stream_error_delegate_;
+  StrictMock<MockDecoderStreamSenderDelegate> decoder_stream_sender_delegate_;
+  QpackDecoder qpack_decoder_;
+  QpackDecodedHeadersAccumulator accumulator_;
+};
+
+// HEADERS frame payload must have a complete Header Block Prefix.
+TEST_F(QpackDecodedHeadersAccumulatorTest, EmptyPayload) {
+  EXPECT_FALSE(accumulator_.EndHeaderBlock());
+  EXPECT_EQ("Incomplete header data prefix.", accumulator_.error_message());
+}
+
+// HEADERS frame payload must have a complete Header Block Prefix.
+TEST_F(QpackDecodedHeadersAccumulatorTest, TruncatedHeaderBlockPrefix) {
+  EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("00")));
+  EXPECT_FALSE(accumulator_.EndHeaderBlock());
+  EXPECT_EQ("Incomplete header data prefix.", accumulator_.error_message());
+}
+
+TEST_F(QpackDecodedHeadersAccumulatorTest, EmptyHeaderList) {
+  EXPECT_CALL(decoder_stream_sender_delegate_,
+              WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+  EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("0000")));
+  EXPECT_TRUE(accumulator_.EndHeaderBlock());
+
+  EXPECT_TRUE(accumulator_.quic_header_list().empty());
+}
+
+// This payload is the prefix of a valid payload, but EndHeaderBlock() is called
+// before it can be completely decoded.
+TEST_F(QpackDecodedHeadersAccumulatorTest, TruncatedPayload) {
+  EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("00002366")));
+  EXPECT_FALSE(accumulator_.EndHeaderBlock());
+  EXPECT_EQ("Incomplete header block.", accumulator_.error_message());
+}
+
+// This payload is invalid because it refers to a non-existing static entry.
+TEST_F(QpackDecodedHeadersAccumulatorTest, InvalidPayload) {
+  EXPECT_FALSE(accumulator_.Decode(QuicTextUtils::HexDecode("0000ff23ff24")));
+  EXPECT_EQ("Static table entry not found.", accumulator_.error_message());
+}
+
+TEST_F(QpackDecodedHeadersAccumulatorTest, Success) {
+  EXPECT_CALL(decoder_stream_sender_delegate_,
+              WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+  QuicString encoded_data(QuicTextUtils::HexDecode("000023666f6f03626172"));
+  EXPECT_TRUE(accumulator_.Decode(encoded_data));
+  EXPECT_TRUE(accumulator_.EndHeaderBlock());
+
+  auto header_list = accumulator_.quic_header_list();
+  auto it = header_list.begin();
+  EXPECT_TRUE(it != header_list.end());
+  EXPECT_EQ("foo", it->first);
+  EXPECT_EQ("bar", it->second);
+  ++it;
+  EXPECT_TRUE(it == header_list.end());
+
+  EXPECT_EQ(strlen("foo") + strlen("bar"),
+            header_list.uncompressed_header_bytes());
+  EXPECT_EQ(encoded_data.size(), header_list.compressed_header_bytes());
+}
+
+}  // namespace test
+}  // namespace quic
diff --git a/quic/core/qpack/qpack_decoder_test.cc b/quic/core/qpack/qpack_decoder_test.cc
index 52f739c..7292c50 100644
--- a/quic/core/qpack/qpack_decoder_test.cc
+++ b/quic/core/qpack/qpack_decoder_test.cc
@@ -62,10 +62,8 @@
   const FragmentMode fragment_mode_;
 };
 
-INSTANTIATE_TEST_CASE_P(,
-                        QpackDecoderTest,
-                        Values(FragmentMode::kSingleChunk,
-                               FragmentMode::kOctetByOctet));
+INSTANTIATE_TEST_SUITE_P(, QpackDecoderTest, Values(FragmentMode::kSingleChunk,
+                                                  FragmentMode::kOctetByOctet));
 
 TEST_P(QpackDecoderTest, NoPrefix) {
   EXPECT_CALL(handler_,
diff --git a/quic/core/qpack/qpack_decoder_test_utils.h b/quic/core/qpack/qpack_decoder_test_utils.h
index 937ecfc..ca5b608 100644
--- a/quic/core/qpack/qpack_decoder_test_utils.h
+++ b/quic/core/qpack/qpack_decoder_test_utils.h
@@ -96,9 +96,9 @@
  public:
   ~NoOpHeadersHandler() override = default;
 
-  void OnHeaderDecoded(QuicStringPiece name, QuicStringPiece value) override{};
-  void OnDecodingCompleted() override{};
-  void OnDecodingErrorDetected(QuicStringPiece error_message) override{};
+  void OnHeaderDecoded(QuicStringPiece name, QuicStringPiece value) override {}
+  void OnDecodingCompleted() override {}
+  void OnDecodingErrorDetected(QuicStringPiece error_message) override {}
 };
 
 void QpackDecode(
diff --git a/quic/core/qpack/qpack_encoder_test.cc b/quic/core/qpack/qpack_encoder_test.cc
index e3dba00..6dea968 100644
--- a/quic/core/qpack/qpack_encoder_test.cc
+++ b/quic/core/qpack/qpack_encoder_test.cc
@@ -37,10 +37,10 @@
   const FragmentMode fragment_mode_;
 };
 
-INSTANTIATE_TEST_CASE_P(,
-                        QpackEncoderTest,
-                        Values(FragmentMode::kSingleChunk,
-                               FragmentMode::kOctetByOctet));
+INSTANTIATE_TEST_SUITE_P(,
+                         QpackEncoderTest,
+                         Values(FragmentMode::kSingleChunk,
+                                FragmentMode::kOctetByOctet));
 
 TEST_P(QpackEncoderTest, Empty) {
   spdy::SpdyHeaderBlock header_list;
diff --git a/quic/core/qpack/qpack_instruction_decoder_test.cc b/quic/core/qpack/qpack_instruction_decoder_test.cc
index d1fe9b5..08557ec 100644
--- a/quic/core/qpack/qpack_instruction_decoder_test.cc
+++ b/quic/core/qpack/qpack_instruction_decoder_test.cc
@@ -101,10 +101,10 @@
   const FragmentMode fragment_mode_;
 };
 
-INSTANTIATE_TEST_CASE_P(,
-                        QpackInstructionDecoderTest,
-                        Values(FragmentMode::kSingleChunk,
-                               FragmentMode::kOctetByOctet));
+INSTANTIATE_TEST_SUITE_P(,
+                         QpackInstructionDecoderTest,
+                         Values(FragmentMode::kSingleChunk,
+                                FragmentMode::kOctetByOctet));
 
 TEST_P(QpackInstructionDecoderTest, SBitAndVarint2) {
   EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1()));
diff --git a/quic/core/qpack/qpack_instruction_encoder_test.cc b/quic/core/qpack/qpack_instruction_encoder_test.cc
index 255520e..006476f 100644
--- a/quic/core/qpack/qpack_instruction_encoder_test.cc
+++ b/quic/core/qpack/qpack_instruction_encoder_test.cc
@@ -43,10 +43,10 @@
   const FragmentMode fragment_mode_;
 };
 
-INSTANTIATE_TEST_CASE_P(,
-                        QpackInstructionEncoderTest,
-                        Values(FragmentMode::kSingleChunk,
-                               FragmentMode::kOctetByOctet));
+INSTANTIATE_TEST_SUITE_P(,
+                         QpackInstructionEncoderTest,
+                         Values(FragmentMode::kSingleChunk,
+                                FragmentMode::kOctetByOctet));
 
 TEST_P(QpackInstructionEncoderTest, Varint) {
   const QpackInstruction instruction{QpackInstructionOpcode{0x00, 0x80},
diff --git a/quic/core/qpack/qpack_round_trip_test.cc b/quic/core/qpack/qpack_round_trip_test.cc
index b9c6300..700ff85 100644
--- a/quic/core/qpack/qpack_round_trip_test.cc
+++ b/quic/core/qpack/qpack_round_trip_test.cc
@@ -56,7 +56,7 @@
   const FragmentMode decoding_fragment_mode_;
 };
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     ,
     QpackRoundTripTest,
     Combine(Values(FragmentMode::kSingleChunk, FragmentMode::kOctetByOctet),
diff --git a/quic/core/quic_arena_scoped_ptr_test.cc b/quic/core/quic_arena_scoped_ptr_test.cc
index 52a8ae2..109fd37 100644
--- a/quic/core/quic_arena_scoped_ptr_test.cc
+++ b/quic/core/quic_arena_scoped_ptr_test.cc
@@ -42,10 +42,10 @@
   QuicOneBlockArena<1024> arena_;
 };
 
-INSTANTIATE_TEST_CASE_P(QuicArenaScopedPtrParamTest,
-                        QuicArenaScopedPtrParamTest,
-                        testing::Values(TestParam::kFromHeap,
-                                        TestParam::kFromArena));
+INSTANTIATE_TEST_SUITE_P(QuicArenaScopedPtrParamTest,
+                         QuicArenaScopedPtrParamTest,
+                         testing::Values(TestParam::kFromHeap,
+                                         TestParam::kFromArena));
 
 TEST_P(QuicArenaScopedPtrParamTest, NullObjects) {
   QuicArenaScopedPtr<TestObject> def;
diff --git a/quic/core/quic_config.cc b/quic/core/quic_config.cc
index ce6f9ea..e53719c 100644
--- a/quic/core/quic_config.cc
+++ b/quic/core/quic_config.cc
@@ -14,6 +14,7 @@
 #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_macros.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
@@ -492,7 +493,7 @@
 }
 
 // TODO(ianswett) Use this for silent close on mobile, or delete.
-ABSL_ATTRIBUTE_UNUSED void QuicConfig::SetSilentClose(bool silent_close) {
+QUIC_UNUSED void QuicConfig::SetSilentClose(bool silent_close) {
   silent_close_.set(silent_close ? 1 : 0, silent_close ? 1 : 0);
 }
 
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index d4c13c4..6346ae0 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -26,6 +26,7 @@
 #include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_error_code_wrappers.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_exported_stats.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
@@ -43,10 +44,6 @@
 
 namespace {
 
-// Maximum number of acks received before sending an ack in response.
-// TODO(fayang): Remove this constant when deprecating QUIC_VERSION_35.
-const QuicPacketCount kMaxPacketsReceivedBeforeAckSend = 20;
-
 // Maximum number of consecutive sent nonretransmittable packets.
 const QuicPacketCount kMaxConsecutiveNonRetransmittablePackets = 19;
 
@@ -63,10 +60,6 @@
 // One eighth RTT delay when doing ack decimation.
 const float kShortAckDecimationDelay = 0.125;
 
-// Error code used in WriteResult to indicate that the packet writer rejected
-// the message as being too big.
-const int kMessageTooBigErrorCode = EMSGSIZE;
-
 // The minimum release time into future in ms.
 const int kMinReleaseTimeIntoFutureMs = 1;
 
@@ -338,9 +331,7 @@
       processing_ack_frame_(false),
       supports_release_time_(false),
       release_time_into_future_(QuicTime::Delta::Zero()),
-      no_version_negotiation_(supported_versions.size() == 1),
-      clear_probing_mark_after_packet_processing_(GetQuicReloadableFlag(
-          quic_clear_probing_mark_after_packet_processing)) {
+      no_version_negotiation_(supported_versions.size() == 1) {
   if (ack_mode_ == ACK_DECIMATION) {
     QUIC_RELOADABLE_FLAG_COUNT(quic_enable_ack_decimation);
   }
@@ -466,8 +457,7 @@
   if (config.HasClientSentConnectionOption(k5RTO, perspective_)) {
     close_connection_after_five_rtos_ = true;
   }
-  if (transport_version() != QUIC_VERSION_35 &&
-      config.HasClientSentConnectionOption(kNSTP, perspective_)) {
+  if (config.HasClientSentConnectionOption(kNSTP, perspective_)) {
     no_stop_waiting_frames_ = true;
   }
   if (config.HasReceivedStatelessResetToken()) {
@@ -688,6 +678,7 @@
   server_supported_versions_ = packet.versions;
 
   if (GetQuicReloadableFlag(quic_no_client_conn_ver_negotiation)) {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_no_client_conn_ver_negotiation);
     CloseConnection(
         QUIC_INVALID_VERSION,
         QuicStrCat(
@@ -815,6 +806,10 @@
   if (level == ENCRYPTION_FORWARD_SECURE &&
       perspective_ == Perspective::IS_SERVER) {
     sent_packet_manager_.SetHandshakeConfirmed();
+    if (sent_packet_manager_.unacked_packets().use_uber_loss_algorithm()) {
+      // This may have changed the retransmission timer, so re-arm it.
+      SetRetransmissionAlarm();
+    }
   }
 }
 
@@ -909,11 +904,11 @@
       return false;
     }
 
-    QUIC_BUG << ENDPOINT
-             << "Received an unencrypted data frame: closing connection"
-             << " packet_number:" << last_header_.packet_number
-             << " stream_id:" << frame.stream_id
-             << " received_packets:" << received_packet_manager_.ack_frame();
+    QUIC_PEER_BUG << ENDPOINT
+                  << "Received an unencrypted data frame: closing connection"
+                  << " packet_number:" << last_header_.packet_number
+                  << " stream_id:" << frame.stream_id << " received_packets:"
+                  << received_packet_manager_.ack_frame();
     CloseConnection(QUIC_UNENCRYPTED_STREAM_DATA,
                     "Unencrypted stream data seen.",
                     ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
@@ -926,8 +921,15 @@
 }
 
 bool QuicConnection::OnCryptoFrame(const QuicCryptoFrame& frame) {
-  // TODO(nharper): Implement.
-  return false;
+  DCHECK(connected_);
+
+  // Since a CRYPTO frame was received, this is not a connectivity probe.
+  // A probe only contains a PING and full padding.
+  UpdatePacketContent(NOT_PADDED_PING);
+
+  visitor_->OnCryptoFrame(frame);
+  should_last_packet_instigate_acks_ = true;
+  return connected_;
 }
 
 bool QuicConnection::OnAckFrameStart(QuicPacketNumber largest_acked,
@@ -1335,7 +1337,8 @@
   if (debug_visitor_ != nullptr) {
     debug_visitor_->OnMessageFrame(frame);
   }
-  visitor_->OnMessageReceived(frame.message_data);
+  visitor_->OnMessageReceived(
+      QuicStringPiece(frame.data, frame.message_length));
   should_last_packet_instigate_acks_ = true;
   return connected_;
 }
@@ -1454,14 +1457,6 @@
 
 void QuicConnection::MaybeQueueAck(bool was_missing) {
   ++num_packets_received_since_last_ack_sent_;
-  // Always send an ack every 20 packets in order to allow the peer to discard
-  // information from the SentPacketManager and provide an RTT measurement.
-  if (transport_version() == QUIC_VERSION_35 &&
-      num_packets_received_since_last_ack_sent_ >=
-          kMaxPacketsReceivedBeforeAckSend) {
-    ack_queued_ = true;
-  }
-
   // Determine whether the newly received packet was missing before recording
   // the received packet.
   if (was_missing) {
@@ -1478,10 +1473,9 @@
   if (should_last_packet_instigate_acks_ && !ack_queued_) {
     ++num_retransmittable_packets_received_since_last_ack_sent_;
     if (ack_mode_ != TCP_ACKING &&
-        // TODO(fayang): Fix this as this check assumes the first received
-        // packet is 1.
-        last_header_.packet_number >
-            QuicPacketNumber(min_received_before_ack_decimation_)) {
+        last_header_.packet_number >=
+            received_packet_manager_.PeerFirstSendingPacketNumber() +
+                min_received_before_ack_decimation_) {
       // Ack up to 10 packets at once unless ack decimation is unlimited.
       if (!unlimited_ack_decimation_ &&
           num_retransmittable_packets_received_since_last_ack_sent_ >=
@@ -1566,7 +1560,9 @@
           sent_packet_manager_.GetLeastUnacked() + max_tracked_packets_) {
     CloseConnection(
         QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS,
-        QuicStrCat("More than ", max_tracked_packets_, " outstanding."),
+        QuicStrCat("More than ", max_tracked_packets_,
+                   " outstanding, least_unacked: ",
+                   sent_packet_manager_.GetLeastUnacked().ToUint64()),
         ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
   }
 }
@@ -1648,6 +1644,18 @@
   pending_version_negotiation_packet_ = false;
 }
 
+size_t QuicConnection::SendCryptoData(EncryptionLevel level,
+                                      size_t write_length,
+                                      QuicStreamOffset offset) {
+  if (write_length == 0) {
+    QUIC_BUG << "Attempt to send empty crypto frame";
+    return 0;
+  }
+
+  ScopedPacketFlusher flusher(this, SEND_ACK_IF_PENDING);
+  return packet_generator_.ConsumeCryptoData(level, write_length, offset);
+}
+
 QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id,
                                                 size_t write_length,
                                                 QuicStreamOffset offset,
@@ -1746,6 +1754,10 @@
   return stats_;
 }
 
+void QuicConnection::OnCoalescedPacket(const QuicEncryptedPacket& packet) {
+  QueueCoalescedPacket(packet);
+}
+
 void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address,
                                       const QuicSocketAddress& peer_address,
                                       const QuicReceivedPacket& packet) {
@@ -1813,13 +1825,9 @@
                   << "Unable to process packet.  Last packet processed: "
                   << last_header_.packet_number;
     current_packet_data_ = nullptr;
-    if (clear_probing_mark_after_packet_processing_) {
-      if (is_current_packet_connectivity_probing_) {
-        QUIC_RELOADABLE_FLAG_COUNT_N(
-            quic_clear_probing_mark_after_packet_processing, 1, 2);
-      }
-      is_current_packet_connectivity_probing_ = false;
-    }
+    is_current_packet_connectivity_probing_ = false;
+
+    MaybeProcessCoalescedPackets();
     return;
   }
 
@@ -1840,17 +1848,12 @@
     }
   }
 
+  MaybeProcessCoalescedPackets();
   MaybeProcessUndecryptablePackets();
   MaybeSendInResponseToPacket();
   SetPingAlarm();
   current_packet_data_ = nullptr;
-  if (clear_probing_mark_after_packet_processing_) {
-    if (is_current_packet_connectivity_probing_) {
-      QUIC_RELOADABLE_FLAG_COUNT_N(
-          quic_clear_probing_mark_after_packet_processing, 2, 2);
-    }
-    is_current_packet_connectivity_probing_ = false;
-  }
+  is_current_packet_connectivity_probing_ = false;
 }
 
 void QuicConnection::OnBlockedWriterCanWrite() {
@@ -1965,16 +1968,17 @@
     // Count those that would have been accepted if FLAGS..random_ipn
     // were true -- to detect/diagnose potential issues prior to
     // enabling the flag.
-    if ((header.packet_number > QuicPacketNumber(1)) &&
-        (header.packet_number <= MaxRandomInitialPacketNumber())) {
+    if (header.packet_number >
+            received_packet_manager_.PeerFirstSendingPacketNumber() &&
+        header.packet_number <= MaxRandomInitialPacketNumber()) {
       QUIC_CODE_COUNT_N(had_possibly_random_ipn, 2, 2);
     }
     bool out_of_bound =
         last_header_.packet_number.IsInitialized()
             ? !Near(header.packet_number, last_header_.packet_number)
-            // TODO(fayang): Fix this as this check assume the first received
-            // packet is 1.
-            : header.packet_number > QuicPacketNumber(kMaxPacketGap);
+            : header.packet_number >=
+                  (received_packet_manager_.PeerFirstSendingPacketNumber() +
+                   kMaxPacketGap);
     if (out_of_bound) {
       QUIC_DLOG(INFO) << ENDPOINT << "Packet " << header.packet_number
                       << " out of bounds.  Discarding";
@@ -2390,8 +2394,7 @@
 
 bool QuicConnection::IsMsgTooBig(const WriteResult& result) {
   return (result.status == WRITE_STATUS_MSG_TOO_BIG) ||
-         (IsWriteError(result.status) &&
-          result.error_code == kMessageTooBigErrorCode);
+         (IsWriteError(result.status) && result.error_code == QUIC_EMSGSIZE);
 }
 
 bool QuicConnection::ShouldDiscardPacket(const SerializedPacket& packet) {
@@ -2426,7 +2429,7 @@
       "Write failed with error: ", error_code, " (", strerror(error_code), ")");
   QUIC_LOG_FIRST_N(ERROR, 2) << ENDPOINT << error_details;
   switch (error_code) {
-    case kMessageTooBigErrorCode:
+    case QUIC_EMSGSIZE:
       CloseConnection(
           QUIC_PACKET_WRITE_ERROR, error_details,
           ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET_WITH_NO_ACK);
@@ -2469,15 +2472,13 @@
     return;
   }
 
-  if (transport_version() != QUIC_VERSION_35) {
-    if (serialized_packet->retransmittable_frames.empty() &&
-        !serialized_packet->original_packet_number.IsInitialized()) {
-      // Increment consecutive_num_packets_with_no_retransmittable_frames_ if
-      // this packet is a new transmission with no retransmittable frames.
-      ++consecutive_num_packets_with_no_retransmittable_frames_;
-    } else {
-      consecutive_num_packets_with_no_retransmittable_frames_ = 0;
-    }
+  if (serialized_packet->retransmittable_frames.empty() &&
+      !serialized_packet->original_packet_number.IsInitialized()) {
+    // Increment consecutive_num_packets_with_no_retransmittable_frames_ if
+    // this packet is a new transmission with no retransmittable frames.
+    ++consecutive_num_packets_with_no_retransmittable_frames_;
+  } else {
+    consecutive_num_packets_with_no_retransmittable_frames_ = 0;
   }
   SendOrQueuePacket(serialized_packet);
 }
@@ -2519,6 +2520,10 @@
 
 void QuicConnection::OnHandshakeComplete() {
   sent_packet_manager_.SetHandshakeConfirmed();
+  if (sent_packet_manager_.unacked_packets().use_uber_loss_algorithm()) {
+    // This may have changed the retransmission timer, so re-arm it.
+    SetRetransmissionAlarm();
+  }
   // The client should immediately ack the SHLO to confirm the handshake is
   // complete with the server.
   if (perspective_ == Perspective::IS_CLIENT && !ack_queued_ &&
@@ -2712,6 +2717,44 @@
   }
 }
 
+void QuicConnection::QueueCoalescedPacket(const QuicEncryptedPacket& packet) {
+  QUIC_DVLOG(1) << ENDPOINT << "Queueing coalesced packet.";
+  coalesced_packets_.push_back(packet.Clone());
+}
+
+void QuicConnection::MaybeProcessCoalescedPackets() {
+  bool processed = false;
+  for (const auto& packet : coalesced_packets_) {
+    if (!connected_) {
+      return;
+    }
+
+    // }
+    // while (connected_ && !coalesced_packets_.empty()) {
+    QUIC_DVLOG(1) << ENDPOINT << "Processing coalesced packet";
+    // QuicEncryptedPacket* packet = coalesced_packets_.front().get();
+    if (framer_.ProcessPacket(*packet)) {
+      processed = true;
+    } else {
+      // If we are unable to decrypt this packet, it might be
+      // because the CHLO or SHLO packet was lost.
+      if (framer_.error() == QUIC_DECRYPTION_FAILURE) {
+        if (encryption_level_ != ENCRYPTION_FORWARD_SECURE &&
+            undecryptable_packets_.size() < max_undecryptable_packets_) {
+          QueueUndecryptablePacket(*packet);
+        } else if (debug_visitor_ != nullptr) {
+          debug_visitor_->OnUndecryptablePacket();
+        }
+      }
+    }
+    // coalesced_packets_.pop_front();
+  }
+  coalesced_packets_.clear();
+  if (processed) {
+    MaybeProcessUndecryptablePackets();
+  }
+}
+
 void QuicConnection::CloseConnection(
     QuicErrorCode error,
     const QuicString& error_details,
@@ -3088,9 +3131,7 @@
       return true;
     }
     if (save_crypto_packets_as_termination_packets_ &&
-        frame.type == STREAM_FRAME &&
-        frame.stream_frame.stream_id ==
-            QuicUtils::GetCryptoStreamId(transport_version())) {
+        QuicUtils::IsHandshakeFrame(frame, transport_version())) {
       return true;
     }
   }
@@ -3535,13 +3576,13 @@
 }
 
 MessageStatus QuicConnection::SendMessage(QuicMessageId message_id,
-                                          QuicStringPiece message) {
+                                          QuicMemSliceSpan message) {
   if (transport_version() <= QUIC_VERSION_44) {
     QUIC_BUG << "MESSAGE frame is not supported for version "
              << transport_version();
     return MESSAGE_STATUS_UNSUPPORTED;
   }
-  if (message.length() > GetLargestMessagePayload()) {
+  if (message.total_length() > GetLargestMessagePayload()) {
     return MESSAGE_STATUS_TOO_LARGE;
   }
   if (!CanWrite(HAS_RETRANSMITTABLE_DATA)) {
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 221754f..d27d0b7 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -96,6 +96,9 @@
   // A simple visitor interface for dealing with a data frame.
   virtual void OnStreamFrame(const QuicStreamFrame& frame) = 0;
 
+  // Called when a CRYPTO frame containing handshake data is received.
+  virtual void OnCryptoFrame(const QuicCryptoFrame& frame) = 0;
+
   // The session should process the WINDOW_UPDATE frame, adjusting both stream
   // and connection level flow control windows.
   virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) = 0;
@@ -231,9 +234,6 @@
   // Called when a StreamFrame has been parsed.
   virtual void OnStreamFrame(const QuicStreamFrame& frame) {}
 
-  // Called when a AckFrame has been parsed.
-  virtual void OnAckFrame(const QuicAckFrame& frame) {}
-
   // Called when a StopWaitingFrame has been parsed.
   virtual void OnStopWaitingFrame(const QuicStopWaitingFrame& frame) {}
 
@@ -380,6 +380,13 @@
   // Sets the number of active streams on the connection for congestion control.
   void SetNumOpenStreams(size_t num_streams);
 
+  // Sends crypto handshake messages of length |write_length| to the peer in as
+  // few packets as possible. Returns the number of bytes consumed from the
+  // data.
+  virtual size_t SendCryptoData(EncryptionLevel level,
+                                size_t write_length,
+                                QuicStreamOffset offset);
+
   // Send the data of length |write_length| to the peer in as few packets as
   // possible. Returns the number of bytes consumed from data, and a boolean
   // indicating if the fin bit was consumed.  This does not indicate the data
@@ -479,6 +486,7 @@
   bool OnUnauthenticatedHeader(const QuicPacketHeader& header) override;
   void OnDecryptedPacket(EncryptionLevel level) override;
   bool OnPacketHeader(const QuicPacketHeader& header) override;
+  void OnCoalescedPacket(const QuicEncryptedPacket& packet) override;
   bool OnStreamFrame(const QuicStreamFrame& frame) override;
   bool OnCryptoFrame(const QuicCryptoFrame& frame) override;
   bool OnAckFrameStart(QuicPacketNumber largest_acked,
@@ -748,7 +756,7 @@
 
   // Tries to send |message| and returns the message status.
   virtual MessageStatus SendMessage(QuicMessageId message_id,
-                                    QuicStringPiece message);
+                                    QuicMemSliceSpan message);
 
   // Returns the largest payload that will fit into a single MESSAGE frame.
   QuicPacketLength GetLargestMessagePayload() const;
@@ -847,6 +855,12 @@
   // Attempts to process any queued undecryptable packets.
   void MaybeProcessUndecryptablePackets();
 
+  // Queue a coalesced packet.
+  void QueueCoalescedPacket(const QuicEncryptedPacket& packet);
+
+  // Process previously queued coalesced packets.
+  void MaybeProcessCoalescedPackets();
+
   enum PacketContent : uint8_t {
     NO_FRAMES_RECEIVED,
     // TODO(fkastenholz): Change name when we get rid of padded ping/
@@ -1156,6 +1170,10 @@
   // sent with the INITIAL encryption and the CHLO message was lost.
   QuicDeque<std::unique_ptr<QuicEncryptedPacket>> undecryptable_packets_;
 
+  // Collection of coalesced packets which were received while processing
+  // the current packet.
+  QuicDeque<std::unique_ptr<QuicEncryptedPacket>> coalesced_packets_;
+
   // Maximum number of undecryptable packets the connection will store.
   size_t max_undecryptable_packets_;
 
@@ -1400,9 +1418,6 @@
   // provided in constructor.
   const bool no_version_negotiation_;
 
-  // Latched value of --quic_clear_probing_mark_after_packet_processing.
-  const bool clear_probing_mark_after_packet_processing_;
-
   // Payload of most recently transmitted QUIC_VERSION_99 connectivity
   // probe packet (the PATH_CHALLENGE payload). This implementation transmits
   // only one PATH_CHALLENGE per connectivity probe, so only one
diff --git a/quic/core/quic_connection_id.cc b/quic/core/quic_connection_id.cc
index a86a5dd..25c5b1f 100644
--- a/quic/core/quic_connection_id.cc
+++ b/quic/core/quic_connection_id.cc
@@ -19,18 +19,9 @@
 
 namespace quic {
 
-QuicConnectionId::QuicConnectionId() {
-  if (!QuicConnectionIdUseNetworkByteOrder()) {
-    id64_ = 0;
-    length_ = sizeof(uint64_t);
-    return;
-  }
-  length_ = 0;
-}
+QuicConnectionId::QuicConnectionId() : length_(0) {}
 
 QuicConnectionId::QuicConnectionId(const char* data, uint8_t length) {
-  QUIC_BUG_IF(!QuicConnectionIdUseNetworkByteOrder())
-      << "new constructor called when flag disabled";
   if (length > kQuicMaxConnectionIdLength) {
     QUIC_BUG << "Attempted to create connection ID of length " << length;
     length = kQuicMaxConnectionIdLength;
@@ -39,48 +30,15 @@
   if (length_ > 0) {
     memcpy(data_, data, length_);
   }
-  QUIC_RESTART_FLAG_COUNT_N(quic_variable_length_connection_ids_server, 2, 3);
-}
-
-QuicConnectionId::QuicConnectionId(uint64_t connection_id64)
-    : length_(sizeof(uint64_t)) {
-  if (!QuicConnectionIdUseNetworkByteOrder()) {
-    id64_ = connection_id64;
-    return;
-  }
-  QUIC_BUG_IF(QuicConnectionIdSupportsVariableLength(Perspective::IS_CLIENT) &&
-              QuicConnectionIdSupportsVariableLength(Perspective::IS_SERVER))
-      << "old constructor called when flag enabled";
-  const uint64_t connection_id64_net = QuicEndian::HostToNet64(connection_id64);
-  memcpy(&data_, &connection_id64_net, sizeof(connection_id64_net));
 }
 
 QuicConnectionId::~QuicConnectionId() {}
 
-uint64_t QuicConnectionId::ToUInt64() const {
-  if (!QuicConnectionIdUseNetworkByteOrder()) {
-    return id64_;
-  }
-  QUIC_BUG_IF(QuicConnectionIdSupportsVariableLength(Perspective::IS_CLIENT) &&
-              QuicConnectionIdSupportsVariableLength(Perspective::IS_SERVER))
-      << "ToUInt64 called when flag enabled";
-  uint64_t connection_id64_net = 0;
-  memcpy(&connection_id64_net, &data_,
-         std::min<size_t>(static_cast<size_t>(length_),
-                          sizeof(connection_id64_net)));
-  return QuicEndian::NetToHost64(connection_id64_net);
-}
-
 const char* QuicConnectionId::data() const {
-  QUIC_BUG_IF(!QuicConnectionIdUseNetworkByteOrder())
-      << "data called when flag disabled";
-  QUIC_RESTART_FLAG_COUNT_N(quic_variable_length_connection_ids_server, 3, 3);
   return data_;
 }
 
 char* QuicConnectionId::mutable_data() {
-  QUIC_BUG_IF(!QuicConnectionIdUseNetworkByteOrder())
-      << "mutable_data called when flag disabled";
   return data_;
 }
 
@@ -89,35 +47,24 @@
 }
 
 void QuicConnectionId::set_length(uint8_t length) {
-  QUIC_BUG_IF(!QuicConnectionIdUseNetworkByteOrder())
-      << "set_length called when flag disabled";
   length_ = length;
 }
 
 bool QuicConnectionId::IsEmpty() const {
-  if (!QuicConnectionIdUseNetworkByteOrder()) {
-    return id64_ == 0;
-  }
   return length_ == 0;
 }
 
 size_t QuicConnectionId::Hash() const {
-  if (!QuicConnectionIdUseNetworkByteOrder()) {
-    return id64_;
-  }
   uint64_t data_bytes[3] = {0, 0, 0};
   static_assert(sizeof(data_bytes) >= sizeof(data_), "sizeof(data_) changed");
   memcpy(data_bytes, data_, length_);
-  // This Hash function is designed to return the same value
-  // as ToUInt64() when the connection ID length is 64 bits.
+  // This Hash function is designed to return the same value as the host byte
+  // order representation when the connection ID length is 64 bits.
   return QuicEndian::NetToHost64(kQuicDefaultConnectionIdLength ^ length_ ^
                                  data_bytes[0] ^ data_bytes[1] ^ data_bytes[2]);
 }
 
 QuicString QuicConnectionId::ToString() const {
-  if (!QuicConnectionIdUseNetworkByteOrder()) {
-    return QuicTextUtils::Uint64ToString(id64_);
-  }
   if (IsEmpty()) {
     return QuicString("0");
   }
@@ -130,9 +77,6 @@
 }
 
 bool QuicConnectionId::operator==(const QuicConnectionId& v) const {
-  if (!QuicConnectionIdUseNetworkByteOrder()) {
-    return id64_ == v.id64_;
-  }
   return length_ == v.length_ && memcmp(data_, v.data_, length_) == 0;
 }
 
@@ -141,9 +85,6 @@
 }
 
 bool QuicConnectionId::operator<(const QuicConnectionId& v) const {
-  if (!QuicConnectionIdUseNetworkByteOrder()) {
-    return id64_ < v.id64_;
-  }
   if (length_ < v.length_) {
     return true;
   }
@@ -157,42 +98,6 @@
   return QuicConnectionId();
 }
 
-QuicConnectionId QuicConnectionIdFromUInt64(uint64_t connection_id64) {
-  return QuicConnectionId(connection_id64);
-}
-
-uint64_t QuicConnectionIdToUInt64(QuicConnectionId connection_id) {
-  return connection_id.ToUInt64();
-}
-
-bool QuicConnectionIdUseNetworkByteOrder() {
-  const bool res = GetQuicRestartFlag(quic_connection_ids_network_byte_order);
-  if (res) {
-    QUIC_RESTART_FLAG_COUNT(quic_connection_ids_network_byte_order);
-  }
-  return res;
-}
-
-bool QuicConnectionIdSupportsVariableLength(Perspective perspective) {
-  if (!QuicConnectionIdUseNetworkByteOrder()) {
-    return false;
-  }
-  bool res;
-  if (perspective == Perspective::IS_SERVER) {
-    res = GetQuicRestartFlag(quic_variable_length_connection_ids_server);
-    if (res) {
-      QUIC_RESTART_FLAG_COUNT_N(quic_variable_length_connection_ids_server, 1,
-                                3);
-    }
-  } else {
-    res = GetQuicRestartFlag(quic_variable_length_connection_ids_client);
-    if (res) {
-      QUIC_RESTART_FLAG_COUNT(quic_variable_length_connection_ids_client);
-    }
-  }
-  return res;
-}
-
 static_assert(kQuicDefaultConnectionIdLength == sizeof(uint64_t),
               "kQuicDefaultConnectionIdLength changed");
 static_assert(kQuicDefaultConnectionIdLength == PACKET_8BYTE_CONNECTION_ID,
diff --git a/quic/core/quic_connection_id.h b/quic/core/quic_connection_id.h
index 5df72ec..06fecfb 100644
--- a/quic/core/quic_connection_id.h
+++ b/quic/core/quic_connection_id.h
@@ -25,17 +25,12 @@
 
 class QUIC_EXPORT_PRIVATE QuicConnectionId {
  public:
-  // Creates a connection ID of length zero, unless the restart flag
-  // quic_connection_ids_network_byte_order is false in which case
-  // it returns an 8-byte all-zeroes connection ID.
+  // Creates a connection ID of length zero.
   QuicConnectionId();
 
   // Creates a connection ID from network order bytes.
   QuicConnectionId(const char* data, uint8_t length);
 
-  // Creator from host byte order uint64_t.
-  explicit QuicConnectionId(uint64_t connection_id64);
-
   ~QuicConnectionId();
 
   // Returns the length of the connection ID, in bytes.
@@ -51,14 +46,9 @@
   // in network byte order.
   char* mutable_data();
 
-  // Returns whether the connection ID has length zero, unless the restart flag
-  // quic_connection_ids_network_byte_order is false in which case
-  // it checks if it is all zeroes.
+  // Returns whether the connection ID has length zero.
   bool IsEmpty() const;
 
-  // Converts to host byte order uint64_t.
-  uint64_t ToUInt64() const;
-
   // Hash() is required to use connection IDs as keys in hash tables.
   size_t Hash() const;
 
@@ -77,11 +67,10 @@
   bool operator<(const QuicConnectionId& v) const;
 
  private:
-  // The connection ID is currently represented in host byte order in |id64_|.
-  // In the future, it will be saved in the first |length_| bytes of |data_|.
+  // The connection ID is represented in network byte order
+  // in the first |length_| bytes of |data_|.
   char data_[kQuicMaxConnectionIdLength];
   uint8_t length_;
-  uint64_t id64_;  // host byte order
 };
 
 // Creates a connection ID of length zero, unless the restart flag
@@ -89,16 +78,6 @@
 // it returns an 8-byte all-zeroes connection ID.
 QUIC_EXPORT_PRIVATE QuicConnectionId EmptyQuicConnectionId();
 
-// Converts connection ID from host-byte-order uint64_t to QuicConnectionId.
-// This is currently the identity function.
-QUIC_EXPORT_PRIVATE QuicConnectionId
-QuicConnectionIdFromUInt64(uint64_t connection_id64);
-
-// Converts connection ID from QuicConnectionId to host-byte-order uint64_t.
-// This is currently the identity function.
-QUIC_EXPORT_PRIVATE uint64_t
-QuicConnectionIdToUInt64(QuicConnectionId connection_id);
-
 // QuicConnectionIdHash can be passed as hash argument to hash tables.
 class QuicConnectionIdHash {
  public:
@@ -107,16 +86,6 @@
   }
 };
 
-// Governs how connection IDs are represented in memory.
-// Checks gfe_restart_flag_quic_connection_ids_network_byte_order.
-QUIC_EXPORT_PRIVATE bool QuicConnectionIdUseNetworkByteOrder();
-
-enum class Perspective : uint8_t;
-// Governs how connection IDs are created.
-// Checks gfe_restart_flag_quic_variable_length_connection_ids_(client|server).
-QUIC_EXPORT_PRIVATE bool QuicConnectionIdSupportsVariableLength(
-    Perspective perspective);
-
 }  // namespace quic
 
 #endif  // QUICHE_QUIC_CORE_QUIC_CONNECTION_ID_H_
diff --git a/quic/core/quic_connection_id_test.cc b/quic/core/quic_connection_id_test.cc
index 017e59e..1c7c761 100644
--- a/quic/core/quic_connection_id_test.cc
+++ b/quic/core/quic_connection_id_test.cc
@@ -37,18 +37,10 @@
 
 TEST_F(QuicConnectionIdTest, ZeroIsNotEmpty) {
   QuicConnectionId connection_id = test::TestConnectionId(0);
-  if (!GetQuicRestartFlag(quic_connection_ids_network_byte_order)) {
-    // Zero is empty when connection IDs are represented in host byte order.
-    return;
-  }
   EXPECT_FALSE(connection_id.IsEmpty());
 }
 
 TEST_F(QuicConnectionIdTest, Data) {
-  if (!GetQuicRestartFlag(quic_connection_ids_network_byte_order)) {
-    // These methods are not allowed when the flag is off.
-    return;
-  }
   char connection_id_data[kQuicDefaultConnectionIdLength];
   memset(connection_id_data, 0x42, sizeof(connection_id_data));
   QuicConnectionId connection_id1 =
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index fe851fd..c56d2c1 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -19,6 +19,7 @@
 #include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
 #include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_error_code_wrappers.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
@@ -102,8 +103,7 @@
 
   bool SetIV(QuicStringPiece iv) override { return true; }
 
-  bool EncryptPacket(QuicTransportVersion /*version*/,
-                     uint64_t packet_number,
+  bool EncryptPacket(uint64_t packet_number,
                      QuicStringPiece associated_data,
                      QuicStringPiece plaintext,
                      char* output,
@@ -167,8 +167,7 @@
     return true;
   }
 
-  bool DecryptPacket(QuicTransportVersion /*version*/,
-                     uint64_t packet_number,
+  bool DecryptPacket(uint64_t packet_number,
                      QuicStringPiece associated_data,
                      QuicStringPiece ciphertext,
                      char* output,
@@ -332,10 +331,10 @@
     }
     if (next_packet_too_large_) {
       next_packet_too_large_ = false;
-      return WriteResult(WRITE_STATUS_ERROR, EMSGSIZE);
+      return WriteResult(WRITE_STATUS_ERROR, QUIC_EMSGSIZE);
     }
     if (always_get_packet_too_large_) {
-      return WriteResult(WRITE_STATUS_ERROR, EMSGSIZE);
+      return WriteResult(WRITE_STATUS_ERROR, QUIC_EMSGSIZE);
     }
     if (IsWriteBlocked()) {
       return WriteResult(is_write_blocked_data_buffered_
@@ -357,10 +356,6 @@
     return WriteResult(WRITE_STATUS_OK, last_packet_size_);
   }
 
-  bool IsWriteBlockedDataBuffered() const override {
-    return is_write_blocked_data_buffered_;
-  }
-
   bool ShouldWriteFail() { return write_should_fail_; }
 
   bool IsWriteBlocked() const override { return write_blocked_; }
@@ -433,6 +428,10 @@
     return framer_.stream_frames();
   }
 
+  const std::vector<std::unique_ptr<QuicCryptoFrame>>& crypto_frames() const {
+    return framer_.crypto_frames();
+  }
+
   const std::vector<QuicPingFrame>& ping_frames() const {
     return framer_.ping_frames();
   }
@@ -643,8 +642,23 @@
   // split needlessly across packet boundaries).  As a result, we have separate
   // tests for some cases for this stream.
   QuicConsumedData SendCryptoStreamData() {
-    return SendStreamDataWithString(
-        QuicUtils::GetCryptoStreamId(transport_version()), "chlo", 0, NO_FIN);
+    QuicStreamOffset offset = 0;
+    QuicStringPiece data("chlo");
+    if (transport_version() < QUIC_VERSION_47) {
+      return SendStreamDataWithString(
+          QuicUtils::GetCryptoStreamId(transport_version()), data, offset,
+          NO_FIN);
+    }
+    producer_.SaveCryptoData(ENCRYPTION_NONE, offset, data);
+    size_t bytes_written;
+    if (notifier_) {
+      bytes_written =
+          notifier_->WriteCryptoData(ENCRYPTION_NONE, data.length(), offset);
+    } else {
+      bytes_written = QuicConnection::SendCryptoData(ENCRYPTION_NONE,
+                                                     data.length(), offset);
+    }
+    return QuicConsumedData(bytes_written, /*fin_consumed*/ false);
   }
 
   void set_version(ParsedQuicVersion version) {
@@ -987,7 +1001,7 @@
     QuicPacketCreatorPeer::FillPacketHeader(&peer_creator_, &header);
     char encrypted_buffer[kMaxPacketSize];
     size_t length = peer_framer_.BuildDataPacket(
-        header, frames, encrypted_buffer, kMaxPacketSize);
+        header, frames, encrypted_buffer, kMaxPacketSize, ENCRYPTION_NONE);
     DCHECK_GT(length, 0u);
 
     const size_t encrypted_length = peer_framer_.EncryptInPlace(
@@ -1135,7 +1149,7 @@
                                          QuicStopWaitingFrame* frame,
                                          EncryptionLevel level) {
     return ProcessFramePacketAtLevel(number, QuicFrame(frame),
-                                     ENCRYPTION_INITIAL);
+                                     ENCRYPTION_ZERO_RTT);
   }
 
   void ProcessGoAwayPacket(QuicGoAwayFrame* frame) {
@@ -1336,9 +1350,9 @@
 };
 
 // Run all end to end tests with all supported versions.
-INSTANTIATE_TEST_CASE_P(SupportedVersion,
-                        QuicConnectionTest,
-                        ::testing::ValuesIn(GetTestParams()));
+INSTANTIATE_TEST_SUITE_P(SupportedVersion,
+                         QuicConnectionTest,
+                         ::testing::ValuesIn(GetTestParams()));
 
 TEST_P(QuicConnectionTest, SelfAddressChangeAtClient) {
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
@@ -1621,9 +1635,6 @@
       connection_.GetStats().num_connectivity_probing_received;
   ProcessReceivedPacket(kSelfAddress, kPeerAddress, *received);
 
-  if (!GetQuicReloadableFlag(quic_clear_probing_mark_after_packet_processing)) {
-    EXPECT_FALSE(connection_.IsCurrentPacketConnectivityProbing());
-  }
   EXPECT_EQ(num_probing_received,
             connection_.GetStats().num_connectivity_probing_received);
   EXPECT_EQ(kPeerAddress, connection_.peer_address());
@@ -1723,9 +1734,6 @@
       connection_.GetStats().num_connectivity_probing_received;
   ProcessReceivedPacket(kSelfAddress, kNewPeerAddress, *received);
 
-  if (!GetQuicReloadableFlag(quic_clear_probing_mark_after_packet_processing)) {
-    EXPECT_TRUE(connection_.IsCurrentPacketConnectivityProbing());
-  }
   EXPECT_EQ(num_probing_received + 1,
             connection_.GetStats().num_connectivity_probing_received);
   EXPECT_EQ(kPeerAddress, connection_.peer_address());
@@ -1786,9 +1794,6 @@
       connection_.GetStats().num_connectivity_probing_received;
   ProcessReceivedPacket(kSelfAddress, kNewPeerAddress, *received);
 
-  if (!GetQuicReloadableFlag(quic_clear_probing_mark_after_packet_processing)) {
-    EXPECT_TRUE(connection_.IsCurrentPacketConnectivityProbing());
-  }
   EXPECT_EQ(num_probing_received + 1,
             connection_.GetStats().num_connectivity_probing_received);
   EXPECT_EQ(kPeerAddress, connection_.peer_address());
@@ -1881,9 +1886,6 @@
       connection_.GetStats().num_connectivity_probing_received;
   ProcessReceivedPacket(kSelfAddress, kPeerAddress, *received);
 
-  if (!GetQuicReloadableFlag(quic_clear_probing_mark_after_packet_processing)) {
-    EXPECT_FALSE(connection_.IsCurrentPacketConnectivityProbing());
-  }
   EXPECT_EQ(num_probing_received,
             connection_.GetStats().num_connectivity_probing_received);
   EXPECT_EQ(kPeerAddress, connection_.peer_address());
@@ -1929,9 +1931,6 @@
       connection_.GetStats().num_connectivity_probing_received;
   ProcessReceivedPacket(kNewSelfAddress, kPeerAddress, *received);
 
-  if (!GetQuicReloadableFlag(quic_clear_probing_mark_after_packet_processing)) {
-    EXPECT_TRUE(connection_.IsCurrentPacketConnectivityProbing());
-  }
   EXPECT_EQ(num_probing_received + 1,
             connection_.GetStats().num_connectivity_probing_received);
   EXPECT_EQ(kPeerAddress, connection_.peer_address());
@@ -1995,6 +1994,13 @@
   header.version_flag = true;
   header.packet_number = QuicPacketNumber(1);
 
+  if (QuicVersionHasLongHeaderLengths(
+          peer_framer_.version().transport_version)) {
+    header.long_packet_type = INITIAL;
+    header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1;
+    header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+  }
+
   QuicFrames frames;
   QuicPaddingFrame padding;
   frames.push_back(QuicFrame(frame1_));
@@ -2028,6 +2034,13 @@
   header.version_flag = true;
   header.packet_number = QuicPacketNumber(1);
 
+  if (QuicVersionHasLongHeaderLengths(
+          peer_framer_.version().transport_version)) {
+    header.long_packet_type = INITIAL;
+    header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1;
+    header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+  }
+
   QuicFrames frames;
   QuicPaddingFrame padding;
   frames.push_back(QuicFrame(frame1_));
@@ -2192,20 +2205,38 @@
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
 
   ProcessPacket(3);
-  // Should ack immediately since we have missing packets.
-  EXPECT_EQ(1u, writer_->packets_write_attempts());
+  if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    // Should not cause an ack.
+    EXPECT_EQ(0u, writer_->packets_write_attempts());
+  } else {
+    // Should ack immediately since we have missing packets.
+    EXPECT_EQ(1u, writer_->packets_write_attempts());
+  }
 
   ProcessPacket(2);
-  // Should ack immediately since we have missing packets.
-  EXPECT_EQ(2u, writer_->packets_write_attempts());
+  if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    // Should ack immediately, since this fills the last hole.
+    EXPECT_EQ(1u, writer_->packets_write_attempts());
+  } else {
+    // Should ack immediately since we have missing packets.
+    EXPECT_EQ(2u, writer_->packets_write_attempts());
+  }
 
   ProcessPacket(1);
   // Should ack immediately, since this fills the last hole.
-  EXPECT_EQ(3u, writer_->packets_write_attempts());
+  if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    EXPECT_EQ(2u, writer_->packets_write_attempts());
+  } else {
+    EXPECT_EQ(3u, writer_->packets_write_attempts());
+  }
 
   ProcessPacket(4);
   // Should not cause an ack.
-  EXPECT_EQ(3u, writer_->packets_write_attempts());
+  if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    EXPECT_EQ(2u, writer_->packets_write_attempts());
+  } else {
+    EXPECT_EQ(3u, writer_->packets_write_attempts());
+  }
 }
 
 TEST_P(QuicConnectionTest, OutOfOrderAckReceiptCausesNoAck) {
@@ -2293,33 +2324,7 @@
   ProcessAckPacket(&frame2);
 }
 
-TEST_P(QuicConnectionTest, 20AcksCausesAckSend) {
-  if (connection_.version().transport_version != QUIC_VERSION_35) {
-    return;
-  }
-  EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
-
-  SendStreamDataToPeer(1, "foo", 0, NO_FIN, nullptr);
-
-  QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_);
-  // But an ack with no missing packets will not send an ack.
-  QuicAckFrame frame = InitAckFrame(1);
-  EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
-  for (int i = 0; i < 19; ++i) {
-    ProcessAckPacket(&frame);
-    EXPECT_FALSE(ack_alarm->IsSet());
-  }
-  EXPECT_EQ(1u, writer_->packets_write_attempts());
-  // The 20th ack packet will cause an ack to be sent.
-  ProcessAckPacket(&frame);
-  EXPECT_EQ(2u, writer_->packets_write_attempts());
-}
-
 TEST_P(QuicConnectionTest, AckSentEveryNthPacket) {
-  if (connection_.version().transport_version == QUIC_VERSION_35) {
-    return;
-  }
-
   connection_.set_ack_frequency_before_ack_decimation(3);
 
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
@@ -2363,10 +2368,6 @@
 }
 
 TEST_P(QuicConnectionTest, AckNeedsRetransmittableFrames) {
-  if (connection_.version().transport_version == QUIC_VERSION_35) {
-    return;
-  }
-
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(99);
 
@@ -2676,10 +2677,14 @@
 
   // Parse the last packet and ensure it's the crypto stream frame.
   EXPECT_EQ(2u, writer_->frame_count());
-  ASSERT_EQ(1u, writer_->stream_frames().size());
   ASSERT_EQ(1u, writer_->padding_frames().size());
-  EXPECT_EQ(QuicUtils::GetCryptoStreamId(connection_.transport_version()),
-            writer_->stream_frames()[0]->stream_id);
+  if (connection_.transport_version() < QUIC_VERSION_47) {
+    ASSERT_EQ(1u, writer_->stream_frames().size());
+    EXPECT_EQ(QuicUtils::GetCryptoStreamId(connection_.transport_version()),
+              writer_->stream_frames()[0]->stream_id);
+  } else {
+    EXPECT_EQ(1u, writer_->crypto_frames().size());
+  }
 }
 
 TEST_P(QuicConnectionTest, FramePackingCryptoThenNonCrypto) {
@@ -3574,9 +3579,9 @@
       NO_FIN, nullptr);
   EXPECT_EQ(0x01010101u, writer_->final_bytes_of_last_packet());
 
-  connection_.SetEncrypter(ENCRYPTION_INITIAL,
+  connection_.SetEncrypter(ENCRYPTION_ZERO_RTT,
                            QuicMakeUnique<TaggingEncrypter>(0x02));
-  connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+  connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
   SendStreamDataToPeer(3, "foo", 0, NO_FIN, nullptr);
   EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet());
 
@@ -3594,7 +3599,7 @@
   // Packet should have been sent with ENCRYPTION_NONE.
   EXPECT_EQ(0x01010101u, writer_->final_bytes_of_previous_packet());
 
-  // Packet should have been sent with ENCRYPTION_INITIAL.
+  // Packet should have been sent with ENCRYPTION_ZERO_RTT.
   EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet());
 }
 
@@ -3615,9 +3620,9 @@
   EXPECT_EQ(1u, connection_.NumQueuedPackets());
 
   // Switch to the new encrypter.
-  connection_.SetEncrypter(ENCRYPTION_INITIAL,
+  connection_.SetEncrypter(ENCRYPTION_ZERO_RTT,
                            QuicMakeUnique<TaggingEncrypter>(0x02));
-  connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+  connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
 
   // Now become writeable and flush the packets.
   writer_->SetWritable();
@@ -3635,9 +3640,7 @@
   connection_.SetEncrypter(ENCRYPTION_NONE,
                            QuicMakeUnique<TaggingEncrypter>(0x01));
   QuicPacketNumber packet_number;
-  SendStreamDataToPeer(
-      QuicUtils::GetCryptoStreamId(connection_.transport_version()), "foo", 0,
-      NO_FIN, &packet_number);
+  connection_.SendCryptoStreamData();
 
   // Simulate the retransmission alarm firing and the socket blocking.
   BlockOnNextWrite();
@@ -3668,9 +3671,9 @@
       QuicUtils::GetCryptoStreamId(connection_.transport_version()), "foo", 0,
       NO_FIN, nullptr);
 
-  connection_.SetEncrypter(ENCRYPTION_INITIAL,
+  connection_.SetEncrypter(ENCRYPTION_ZERO_RTT,
                            QuicMakeUnique<TaggingEncrypter>(0x02));
-  connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+  connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
 
   SendStreamDataToPeer(2, "bar", 0, NO_FIN, nullptr);
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
@@ -3687,27 +3690,27 @@
   use_tagging_decrypter();
 
   const uint8_t tag = 0x07;
-  peer_framer_.SetEncrypter(ENCRYPTION_INITIAL,
+  peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
                             QuicMakeUnique<TaggingEncrypter>(tag));
 
   // Process an encrypted packet which can not yet be decrypted which should
   // result in the packet being buffered.
-  ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_INITIAL);
+  ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
 
   // Transition to the new encryption state and process another encrypted packet
   // which should result in the original packet being processed.
-  connection_.SetDecrypter(ENCRYPTION_INITIAL,
+  connection_.SetDecrypter(ENCRYPTION_ZERO_RTT,
                            QuicMakeUnique<StrictTaggingDecrypter>(tag));
-  connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
-  connection_.SetEncrypter(ENCRYPTION_INITIAL,
+  connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+  connection_.SetEncrypter(ENCRYPTION_ZERO_RTT,
                            QuicMakeUnique<TaggingEncrypter>(tag));
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(2);
-  ProcessDataPacketAtLevel(2, !kHasStopWaiting, ENCRYPTION_INITIAL);
+  ProcessDataPacketAtLevel(2, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
 
   // Finally, process a third packet and note that we do not reprocess the
   // buffered packet.
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
-  ProcessDataPacketAtLevel(3, !kHasStopWaiting, ENCRYPTION_INITIAL);
+  ProcessDataPacketAtLevel(3, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
 }
 
 TEST_P(QuicConnectionTest, TestRetransmitOrder) {
@@ -3752,23 +3755,23 @@
   use_tagging_decrypter();
 
   const uint8_t tag = 0x07;
-  peer_framer_.SetEncrypter(ENCRYPTION_INITIAL,
+  peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
                             QuicMakeUnique<TaggingEncrypter>(tag));
 
   // Process an encrypted packet which can not yet be decrypted which should
   // result in the packet being buffered.
   for (uint64_t i = 1; i <= 100; ++i) {
-    ProcessDataPacketAtLevel(i, !kHasStopWaiting, ENCRYPTION_INITIAL);
+    ProcessDataPacketAtLevel(i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
   }
 
   // Transition to the new encryption state and process another encrypted packet
   // which should result in the original packets being processed.
   EXPECT_FALSE(connection_.GetProcessUndecryptablePacketsAlarm()->IsSet());
-  connection_.SetDecrypter(ENCRYPTION_INITIAL,
+  connection_.SetDecrypter(ENCRYPTION_ZERO_RTT,
                            QuicMakeUnique<StrictTaggingDecrypter>(tag));
   EXPECT_TRUE(connection_.GetProcessUndecryptablePacketsAlarm()->IsSet());
-  connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
-  connection_.SetEncrypter(ENCRYPTION_INITIAL,
+  connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+  connection_.SetEncrypter(ENCRYPTION_ZERO_RTT,
                            QuicMakeUnique<TaggingEncrypter>(tag));
 
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(100);
@@ -3777,7 +3780,7 @@
   // Finally, process a third packet and note that we do not reprocess the
   // buffered packet.
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
-  ProcessDataPacketAtLevel(102, !kHasStopWaiting, ENCRYPTION_INITIAL);
+  ProcessDataPacketAtLevel(102, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
 }
 
 TEST_P(QuicConnectionTest, SetRTOAfterWritingToSocket) {
@@ -4932,7 +4935,9 @@
       connection_.version().transport_version, kIncludeVersion,
       !kIncludeDiversificationNonce, PACKET_8BYTE_CONNECTION_ID,
       PACKET_0BYTE_CONNECTION_ID,
-      QuicPacketCreatorPeer::GetPacketNumberLength(creator_), &payload_length);
+      QuicPacketCreatorPeer::GetPacketNumberLength(creator_),
+      QuicPacketCreatorPeer::GetRetryTokenLengthLength(creator_),
+      QuicPacketCreatorPeer::GetLengthLength(creator_), &payload_length);
   connection_.SetMaxPacketLength(length);
 
   // Queue the first packet.
@@ -4959,7 +4964,9 @@
       connection_.version().transport_version, kIncludeVersion,
       !kIncludeDiversificationNonce, PACKET_8BYTE_CONNECTION_ID,
       PACKET_0BYTE_CONNECTION_ID,
-      QuicPacketCreatorPeer::GetPacketNumberLength(creator_), &payload_length);
+      QuicPacketCreatorPeer::GetPacketNumberLength(creator_),
+      QuicPacketCreatorPeer::GetRetryTokenLengthLength(creator_),
+      QuicPacketCreatorPeer::GetLengthLength(creator_), &payload_length);
   // GetPacketLengthForOneStream() assumes a stream offset of 0 in determining
   // packet length. The size of the offset field in a stream frame is
   // 0 for offset 0, and 2 for non-zero offsets up through 16K (for
@@ -5049,17 +5056,17 @@
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
   const uint8_t tag = 0x07;
-  connection_.SetDecrypter(ENCRYPTION_INITIAL,
+  connection_.SetDecrypter(ENCRYPTION_ZERO_RTT,
                            QuicMakeUnique<StrictTaggingDecrypter>(tag));
-  peer_framer_.SetEncrypter(ENCRYPTION_INITIAL,
+  peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
                             QuicMakeUnique<TaggingEncrypter>(tag));
   // Process a packet from the non-crypto stream.
   frame1_.stream_id = 3;
 
-  // The same as ProcessPacket(1) except that ENCRYPTION_INITIAL is used
+  // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used
   // instead of ENCRYPTION_NONE.
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
-  ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_INITIAL);
+  ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
 
   // Check if delayed ack timer is running for the expected interval.
   EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
@@ -5087,17 +5094,17 @@
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
   const uint8_t tag = 0x07;
-  connection_.SetDecrypter(ENCRYPTION_INITIAL,
+  connection_.SetDecrypter(ENCRYPTION_ZERO_RTT,
                            QuicMakeUnique<StrictTaggingDecrypter>(tag));
-  peer_framer_.SetEncrypter(ENCRYPTION_INITIAL,
+  peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
                             QuicMakeUnique<TaggingEncrypter>(tag));
   // Process a packet from the non-crypto stream.
   frame1_.stream_id = 3;
 
-  // The same as ProcessPacket(1) except that ENCRYPTION_INITIAL is used
+  // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used
   // instead of ENCRYPTION_NONE.
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
-  ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_INITIAL);
+  ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
 
   // Check if delayed ack timer is running for the expected interval.
   EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
@@ -5119,7 +5126,7 @@
   // ack alarm to be set delayed ack time in the future.
   ack_time = clock_.ApproximateNow() + DefaultDelayedAckTime();
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
-  ProcessDataPacketAtLevel(2, !kHasStopWaiting, ENCRYPTION_INITIAL);
+  ProcessDataPacketAtLevel(2, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
 
   // Check if delayed ack timer is running for the expected interval.
   EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
@@ -5141,7 +5148,7 @@
   clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
   ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1);
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
-  ProcessDataPacketAtLevel(3, !kHasStopWaiting, ENCRYPTION_INITIAL);
+  ProcessDataPacketAtLevel(3, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
 
   // Check if delayed ack timer is running for the expected interval.
   EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
@@ -5163,9 +5170,9 @@
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
   const uint8_t tag = 0x07;
-  connection_.SetDecrypter(ENCRYPTION_INITIAL,
+  connection_.SetDecrypter(ENCRYPTION_ZERO_RTT,
                            QuicMakeUnique<StrictTaggingDecrypter>(tag));
-  peer_framer_.SetEncrypter(ENCRYPTION_INITIAL,
+  peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
                             QuicMakeUnique<TaggingEncrypter>(tag));
   // Process a packet from the non-crypto stream.
   frame1_.stream_id = 3;
@@ -5174,14 +5181,14 @@
   uint64_t kFirstDecimatedPacket = 101;
   for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
-    ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_INITIAL);
+    ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
   }
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
-  // The same as ProcessPacket(1) except that ENCRYPTION_INITIAL is used
+  // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used
   // instead of ENCRYPTION_NONE.
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
   ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting,
-                           ENCRYPTION_INITIAL);
+                           ENCRYPTION_ZERO_RTT);
 
   // Check if delayed ack timer is running for the expected interval.
   EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
@@ -5192,7 +5199,7 @@
     EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
     ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting,
-                             ENCRYPTION_INITIAL);
+                             ENCRYPTION_ZERO_RTT);
   }
   // Check that ack is sent and that delayed ack alarm is reset.
   if (GetParam().no_stop_waiting) {
@@ -5222,17 +5229,17 @@
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
   const uint8_t tag = 0x07;
-  connection_.SetDecrypter(ENCRYPTION_INITIAL,
+  connection_.SetDecrypter(ENCRYPTION_ZERO_RTT,
                            QuicMakeUnique<StrictTaggingDecrypter>(tag));
-  peer_framer_.SetEncrypter(ENCRYPTION_INITIAL,
+  peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
                             QuicMakeUnique<TaggingEncrypter>(tag));
   // Process a packet from the non-crypto stream.
   frame1_.stream_id = 3;
 
-  // The same as ProcessPacket(1) except that ENCRYPTION_INITIAL is used
+  // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used
   // instead of ENCRYPTION_NONE.
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
-  ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_INITIAL);
+  ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
 
   // Check if delayed ack timer is running for the expected interval.
   EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
@@ -5254,7 +5261,7 @@
   // ack alarm to be set delayed ack time in the future.
   ack_time = clock_.ApproximateNow() + DefaultDelayedAckTime();
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
-  ProcessDataPacketAtLevel(2, !kHasStopWaiting, ENCRYPTION_INITIAL);
+  ProcessDataPacketAtLevel(2, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
 
   // Check if delayed ack timer is running for the expected interval.
   EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
@@ -5276,7 +5283,7 @@
   clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
   ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1);
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
-  ProcessDataPacketAtLevel(3, !kHasStopWaiting, ENCRYPTION_INITIAL);
+  ProcessDataPacketAtLevel(3, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
 
   // Check if delayed ack timer is running for the expected interval.
   EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
@@ -5290,14 +5297,14 @@
   uint64_t kFirstDecimatedPacket = 101;
   for (unsigned int i = 0; i < kFirstDecimatedPacket - 4; ++i) {
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
-    ProcessDataPacketAtLevel(4 + i, !kHasStopWaiting, ENCRYPTION_INITIAL);
+    ProcessDataPacketAtLevel(4 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
   }
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
-  // The same as ProcessPacket(1) except that ENCRYPTION_INITIAL is used
+  // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used
   // instead of ENCRYPTION_NONE.
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
   ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting,
-                           ENCRYPTION_INITIAL);
+                           ENCRYPTION_ZERO_RTT);
 
   // Check if delayed ack timer is running for the expected interval.
   EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
@@ -5308,7 +5315,7 @@
     EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
     ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting,
-                             ENCRYPTION_INITIAL);
+                             ENCRYPTION_ZERO_RTT);
   }
   // Check that ack is sent and that delayed ack alarm is reset.
   if (GetParam().no_stop_waiting) {
@@ -5326,7 +5333,7 @@
   ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1);
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
   ProcessDataPacketAtLevel(kFirstDecimatedPacket + 10, !kHasStopWaiting,
-                           ENCRYPTION_INITIAL);
+                           ENCRYPTION_ZERO_RTT);
 
   // Check if delayed ack timer is running for the expected interval.
   EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
@@ -5355,9 +5362,9 @@
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
   const uint8_t tag = 0x07;
-  connection_.SetDecrypter(ENCRYPTION_INITIAL,
+  connection_.SetDecrypter(ENCRYPTION_ZERO_RTT,
                            QuicMakeUnique<StrictTaggingDecrypter>(tag));
-  peer_framer_.SetEncrypter(ENCRYPTION_INITIAL,
+  peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
                             QuicMakeUnique<TaggingEncrypter>(tag));
   // Process a packet from the non-crypto stream.
   frame1_.stream_id = 3;
@@ -5366,14 +5373,14 @@
   uint64_t kFirstDecimatedPacket = 101;
   for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
-    ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_INITIAL);
+    ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
   }
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
-  // The same as ProcessPacket(1) except that ENCRYPTION_INITIAL is used
+  // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used
   // instead of ENCRYPTION_NONE.
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
   ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting,
-                           ENCRYPTION_INITIAL);
+                           ENCRYPTION_ZERO_RTT);
 
   // Check if delayed ack timer is running for the expected interval.
   EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
@@ -5385,7 +5392,7 @@
     EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
     ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting,
-                             ENCRYPTION_INITIAL);
+                             ENCRYPTION_ZERO_RTT);
   }
   // The delayed ack timer should still be set to the expected deadline.
   EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
@@ -5408,9 +5415,9 @@
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
   const uint8_t tag = 0x07;
-  connection_.SetDecrypter(ENCRYPTION_INITIAL,
+  connection_.SetDecrypter(ENCRYPTION_ZERO_RTT,
                            QuicMakeUnique<StrictTaggingDecrypter>(tag));
-  peer_framer_.SetEncrypter(ENCRYPTION_INITIAL,
+  peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
                             QuicMakeUnique<TaggingEncrypter>(tag));
   // Process a packet from the non-crypto stream.
   frame1_.stream_id = 3;
@@ -5419,14 +5426,14 @@
   uint64_t kFirstDecimatedPacket = 101;
   for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
-    ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_INITIAL);
+    ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
   }
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
-  // The same as ProcessPacket(1) except that ENCRYPTION_INITIAL is used
+  // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used
   // instead of ENCRYPTION_NONE.
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
   ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting,
-                           ENCRYPTION_INITIAL);
+                           ENCRYPTION_ZERO_RTT);
 
   // Check if delayed ack timer is running for the expected interval.
   EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
@@ -5437,7 +5444,7 @@
     EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
     ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting,
-                             ENCRYPTION_INITIAL);
+                             ENCRYPTION_ZERO_RTT);
   }
   // Check that ack is sent and that delayed ack alarm is reset.
   if (GetParam().no_stop_waiting) {
@@ -5471,9 +5478,9 @@
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
   const uint8_t tag = 0x07;
-  connection_.SetDecrypter(ENCRYPTION_INITIAL,
+  connection_.SetDecrypter(ENCRYPTION_ZERO_RTT,
                            QuicMakeUnique<StrictTaggingDecrypter>(tag));
-  peer_framer_.SetEncrypter(ENCRYPTION_INITIAL,
+  peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
                             QuicMakeUnique<TaggingEncrypter>(tag));
   // Process a packet from the non-crypto stream.
   frame1_.stream_id = 3;
@@ -5482,7 +5489,7 @@
   uint64_t kFirstDecimatedPacket = 101;
   for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
-    ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_INITIAL);
+    ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
   }
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
 
@@ -5492,7 +5499,7 @@
     // Process packet 10 first and ensure the alarm is one eighth min_rtt.
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
     ProcessDataPacketAtLevel(kFirstDecimatedPacket + 9 + (j * 11),
-                             !kHasStopWaiting, ENCRYPTION_INITIAL);
+                             !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
     ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(5);
     EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
     EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
@@ -5505,7 +5512,7 @@
       // The ACK shouldn't be sent until the 10th packet is processed.
       EXPECT_TRUE(writer_->ack_frames().empty());
       ProcessDataPacketAtLevel(kFirstDecimatedPacket + i + (j * 11),
-                               !kHasStopWaiting, ENCRYPTION_INITIAL);
+                               !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
     }
     // Check that ack is sent and that delayed ack alarm is reset.
     if (GetParam().no_stop_waiting) {
@@ -5540,9 +5547,9 @@
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
   const uint8_t tag = 0x07;
-  connection_.SetDecrypter(ENCRYPTION_INITIAL,
+  connection_.SetDecrypter(ENCRYPTION_ZERO_RTT,
                            QuicMakeUnique<StrictTaggingDecrypter>(tag));
-  peer_framer_.SetEncrypter(ENCRYPTION_INITIAL,
+  peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
                             QuicMakeUnique<TaggingEncrypter>(tag));
   // Process a packet from the non-crypto stream.
   frame1_.stream_id = 3;
@@ -5551,14 +5558,14 @@
   uint64_t kFirstDecimatedPacket = 101;
   for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
-    ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_INITIAL);
+    ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
   }
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
-  // The same as ProcessPacket(1) except that ENCRYPTION_INITIAL is used
+  // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used
   // instead of ENCRYPTION_NONE.
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
   ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting,
-                           ENCRYPTION_INITIAL);
+                           ENCRYPTION_ZERO_RTT);
 
   // Check if delayed ack timer is running for the expected interval.
   EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
@@ -5567,7 +5574,7 @@
   // Process packet 10 first and ensure the alarm is one eighth min_rtt.
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
   ProcessDataPacketAtLevel(kFirstDecimatedPacket + 19, !kHasStopWaiting,
-                           ENCRYPTION_INITIAL);
+                           ENCRYPTION_ZERO_RTT);
   ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(5);
   EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
   EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
@@ -5577,7 +5584,7 @@
     EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
     ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting,
-                             ENCRYPTION_INITIAL);
+                             ENCRYPTION_ZERO_RTT);
   }
   // Check that ack is sent and that delayed ack alarm is reset.
   if (GetParam().no_stop_waiting) {
@@ -5595,7 +5602,7 @@
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
   ProcessDataPacketAtLevel(kFirstDecimatedPacket + 10, !kHasStopWaiting,
-                           ENCRYPTION_INITIAL);
+                           ENCRYPTION_ZERO_RTT);
   // Check that ack is sent and that delayed ack alarm is reset.
   if (GetParam().no_stop_waiting) {
     EXPECT_EQ(1u, writer_->frame_count());
@@ -5629,9 +5636,9 @@
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
   const uint8_t tag = 0x07;
-  connection_.SetDecrypter(ENCRYPTION_INITIAL,
+  connection_.SetDecrypter(ENCRYPTION_ZERO_RTT,
                            QuicMakeUnique<StrictTaggingDecrypter>(tag));
-  peer_framer_.SetEncrypter(ENCRYPTION_INITIAL,
+  peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
                             QuicMakeUnique<TaggingEncrypter>(tag));
   // Process a packet from the non-crypto stream.
   frame1_.stream_id = 3;
@@ -5640,14 +5647,14 @@
   uint64_t kFirstDecimatedPacket = 101;
   for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
-    ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_INITIAL);
+    ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
   }
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
-  // The same as ProcessPacket(1) except that ENCRYPTION_INITIAL is used
+  // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used
   // instead of ENCRYPTION_NONE.
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
   ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting,
-                           ENCRYPTION_INITIAL);
+                           ENCRYPTION_ZERO_RTT);
 
   // Check if delayed ack timer is running for the expected interval.
   EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
@@ -5656,7 +5663,7 @@
   // Process packet 10 first and ensure the alarm is one eighth min_rtt.
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
   ProcessDataPacketAtLevel(kFirstDecimatedPacket + 9, !kHasStopWaiting,
-                           ENCRYPTION_INITIAL);
+                           ENCRYPTION_ZERO_RTT);
   ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(5);
   EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
   EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
@@ -5666,7 +5673,7 @@
     EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
     ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting,
-                             ENCRYPTION_INITIAL);
+                             ENCRYPTION_ZERO_RTT);
   }
   // Check that ack is sent and that delayed ack alarm is reset.
   if (GetParam().no_stop_waiting) {
@@ -5702,9 +5709,9 @@
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
   const uint8_t tag = 0x07;
-  connection_.SetDecrypter(ENCRYPTION_INITIAL,
+  connection_.SetDecrypter(ENCRYPTION_ZERO_RTT,
                            QuicMakeUnique<StrictTaggingDecrypter>(tag));
-  peer_framer_.SetEncrypter(ENCRYPTION_INITIAL,
+  peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
                             QuicMakeUnique<TaggingEncrypter>(tag));
   // Process a packet from the non-crypto stream.
   frame1_.stream_id = 3;
@@ -5713,14 +5720,14 @@
   uint64_t kFirstDecimatedPacket = 101;
   for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
-    ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_INITIAL);
+    ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
   }
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
-  // The same as ProcessPacket(1) except that ENCRYPTION_INITIAL is used
+  // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used
   // instead of ENCRYPTION_NONE.
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
   ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting,
-                           ENCRYPTION_INITIAL);
+                           ENCRYPTION_ZERO_RTT);
 
   // Check if delayed ack timer is running for the expected interval.
   EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
@@ -5729,7 +5736,7 @@
   // Process packet 10 first and ensure the alarm is one eighth min_rtt.
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
   ProcessDataPacketAtLevel(kFirstDecimatedPacket + 19, !kHasStopWaiting,
-                           ENCRYPTION_INITIAL);
+                           ENCRYPTION_ZERO_RTT);
   ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(5);
   EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
   EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
@@ -5739,7 +5746,7 @@
     EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
     ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting,
-                             ENCRYPTION_INITIAL);
+                             ENCRYPTION_ZERO_RTT);
   }
   // Check that ack is sent and that delayed ack alarm is reset.
   if (GetParam().no_stop_waiting) {
@@ -5757,7 +5764,7 @@
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
   ProcessDataPacketAtLevel(kFirstDecimatedPacket + 10, !kHasStopWaiting,
-                           ENCRYPTION_INITIAL);
+                           ENCRYPTION_ZERO_RTT);
   // Check that ack is sent and that delayed ack alarm is reset.
   if (GetParam().no_stop_waiting) {
     EXPECT_EQ(1u, writer_->frame_count());
@@ -5810,23 +5817,46 @@
 TEST_P(QuicConnectionTest, NoAckOnOldNacks) {
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   // Drop one packet, triggering a sequence of acks.
+  if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+  } else {
+    EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+  }
   ProcessPacket(2);
   size_t frames_per_ack = GetParam().no_stop_waiting ? 1 : 2;
-  EXPECT_EQ(frames_per_ack, writer_->frame_count());
-  EXPECT_FALSE(writer_->ack_frames().empty());
-  writer_->Reset();
+  if (!GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    EXPECT_EQ(frames_per_ack, writer_->frame_count());
+    EXPECT_FALSE(writer_->ack_frames().empty());
+    writer_->Reset();
+  }
+
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
   ProcessPacket(3);
   EXPECT_EQ(frames_per_ack, writer_->frame_count());
   EXPECT_FALSE(writer_->ack_frames().empty());
   writer_->Reset();
+
+  if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+  } else {
+    EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+  }
   ProcessPacket(4);
-  EXPECT_EQ(frames_per_ack, writer_->frame_count());
-  EXPECT_FALSE(writer_->ack_frames().empty());
-  writer_->Reset();
+  if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    EXPECT_EQ(0u, writer_->frame_count());
+  } else {
+    EXPECT_EQ(frames_per_ack, writer_->frame_count());
+    EXPECT_FALSE(writer_->ack_frames().empty());
+    writer_->Reset();
+  }
+
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
   ProcessPacket(5);
   EXPECT_EQ(frames_per_ack, writer_->frame_count());
   EXPECT_FALSE(writer_->ack_frames().empty());
   writer_->Reset();
+
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
   // Now only set the timer on the 6th packet, instead of sending another ack.
   ProcessPacket(6);
   EXPECT_EQ(0u, writer_->frame_count());
@@ -5904,7 +5934,11 @@
     EXPECT_EQ(4u, writer_->frame_count());
     EXPECT_FALSE(writer_->stop_waiting_frames().empty());
   }
-  EXPECT_EQ(1u, writer_->stream_frames().size());
+  if (connection_.transport_version() < QUIC_VERSION_47) {
+    EXPECT_EQ(1u, writer_->stream_frames().size());
+  } else {
+    EXPECT_EQ(1u, writer_->crypto_frames().size());
+  }
   EXPECT_EQ(1u, writer_->padding_frames().size());
   ASSERT_FALSE(writer_->ack_frames().empty());
   EXPECT_EQ(QuicPacketNumber(2u), LargestAcked(writer_->ack_frames().front()));
@@ -5933,7 +5967,11 @@
     EXPECT_EQ(4u, writer_->frame_count());
     EXPECT_FALSE(writer_->stop_waiting_frames().empty());
   }
-  EXPECT_EQ(1u, writer_->stream_frames().size());
+  if (connection_.transport_version() < QUIC_VERSION_47) {
+    EXPECT_EQ(1u, writer_->stream_frames().size());
+  } else {
+    EXPECT_EQ(1u, writer_->crypto_frames().size());
+  }
   EXPECT_EQ(1u, writer_->padding_frames().size());
   ASSERT_FALSE(writer_->ack_frames().empty());
   EXPECT_EQ(QuicPacketNumber(2u), LargestAcked(writer_->ack_frames().front()));
@@ -6214,6 +6252,13 @@
   header.version_flag = true;
   header.packet_number = QuicPacketNumber(12);
 
+  if (QuicVersionHasLongHeaderLengths(
+          peer_framer_.version().transport_version)) {
+    header.long_packet_type = INITIAL;
+    header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1;
+    header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+  }
+
   QuicFrames frames;
   frames.push_back(QuicFrame(frame1_));
   std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames));
@@ -6259,6 +6304,13 @@
   header.version_flag = true;
   header.packet_number = QuicPacketNumber(12);
 
+  if (QuicVersionHasLongHeaderLengths(
+          peer_framer_.version().transport_version)) {
+    header.long_packet_type = INITIAL;
+    header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1;
+    header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+  }
+
   QuicFrames frames;
   frames.push_back(QuicFrame(frame1_));
   std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames));
@@ -6311,6 +6363,13 @@
   header.version_flag = true;
   header.packet_number = QuicPacketNumber(12);
 
+  if (QuicVersionHasLongHeaderLengths(
+          peer_framer_.version().transport_version)) {
+    header.long_packet_type = INITIAL;
+    header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1;
+    header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+  }
+
   QuicFrames frames;
   frames.push_back(QuicFrame(frame1_));
   std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames));
@@ -7586,6 +7645,7 @@
   }
   QuicString message(connection_.GetLargestMessagePayload() * 2, 'a');
   QuicStringPiece message_data(message);
+  QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
   {
     QuicConnection::ScopedPacketFlusher flusher(&connection_,
                                                 QuicConnection::SEND_ACK);
@@ -7594,22 +7654,32 @@
     // get sent, one contains stream frame, and the other only contains the
     // message frame.
     EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
-    EXPECT_EQ(MESSAGE_STATUS_SUCCESS,
-              connection_.SendMessage(
-                  1, QuicStringPiece(message_data.data(),
-                                     connection_.GetLargestMessagePayload())));
+    EXPECT_EQ(
+        MESSAGE_STATUS_SUCCESS,
+        connection_.SendMessage(
+            1, MakeSpan(connection_.helper()->GetStreamSendBufferAllocator(),
+                        QuicStringPiece(message_data.data(),
+                                        connection_.GetLargestMessagePayload()),
+                        &storage)));
   }
   // Fail to send a message if connection is congestion control blocked.
   EXPECT_CALL(*send_algorithm_, CanSend(_)).WillOnce(Return(false));
-  EXPECT_EQ(MESSAGE_STATUS_BLOCKED, connection_.SendMessage(2, "message"));
+  EXPECT_EQ(
+      MESSAGE_STATUS_BLOCKED,
+      connection_.SendMessage(
+          2, MakeSpan(connection_.helper()->GetStreamSendBufferAllocator(),
+                      "message", &storage)));
 
   // Always fail to send a message which cannot fit into one packet.
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
   EXPECT_EQ(
       MESSAGE_STATUS_TOO_LARGE,
       connection_.SendMessage(
-          3, QuicStringPiece(message_data.data(),
-                             connection_.GetLargestMessagePayload() + 1)));
+          3,
+          MakeSpan(connection_.helper()->GetStreamSendBufferAllocator(),
+                   QuicStringPiece(message_data.data(),
+                                   connection_.GetLargestMessagePayload() + 1),
+                   &storage)));
 }
 
 // Test to check that the path challenge/path response logic works
diff --git a/quic/core/quic_control_frame_manager.cc b/quic/core/quic_control_frame_manager.cc
index c2b5192..8d4ba53 100644
--- a/quic/core/quic_control_frame_manager.cc
+++ b/quic/core/quic_control_frame_manager.cc
@@ -88,25 +88,6 @@
       new QuicStopSendingFrame(++last_control_frame_id_, stream_id, code)));
 }
 
-void QuicControlFrameManager::WriteOrBufferRstStreamStopSending(
-    QuicStreamId stream_id,
-    QuicRstStreamErrorCode error_code,
-    QuicStreamOffset bytes_written) {
-  const bool had_buffered_frames = HasBufferedFrames();
-  QUIC_DVLOG(1) << "Queuing RST_STREAM_FRAME";
-  control_frames_.emplace_back(QuicFrame(new QuicRstStreamFrame(
-      ++last_control_frame_id_, stream_id, error_code, bytes_written)));
-  if (session_->connection()->transport_version() == QUIC_VERSION_99) {
-    QUIC_DVLOG(1) << "Version 99, Queuing STOP_SENDING";
-    control_frames_.emplace_back(QuicFrame(new QuicStopSendingFrame(
-        ++last_control_frame_id_, stream_id, error_code)));
-  }
-  if (had_buffered_frames) {
-    return;
-  }
-  WriteBufferedFrames();
-}
-
 void QuicControlFrameManager::WritePing() {
   QUIC_DVLOG(1) << "Writing PING_FRAME";
   if (HasBufferedFrames()) {
diff --git a/quic/core/quic_control_frame_manager.h b/quic/core/quic_control_frame_manager.h
index ace08c6..618f3f9 100644
--- a/quic/core/quic_control_frame_manager.h
+++ b/quic/core/quic_control_frame_manager.h
@@ -61,13 +61,6 @@
   // immediately.
   void WriteOrBufferMaxStreamId(QuicStreamId id);
 
-  // Tries to send a packet with both a RST_STREAM and, if version 99, an
-  // IETF-QUIC STOP_SENDING frame. The frames are buffered if they can not
-  // be sent immediately.
-  void WriteOrBufferRstStreamStopSending(QuicControlFrameId stream_id,
-                                         QuicRstStreamErrorCode error_code,
-                                         QuicStreamOffset bytes_written);
-
   // Tries to send an IETF-QUIC STOP_SENDING frame. The frame is buffered if it
   // can not be sent immediately.
   void WriteOrBufferStopSending(uint16_t code, QuicStreamId stream_id);
diff --git a/quic/core/quic_crypto_client_handshaker.cc b/quic/core/quic_crypto_client_handshaker.cc
index 81487df..43948c2 100644
--- a/quic/core/quic_crypto_client_handshaker.cc
+++ b/quic/core/quic_crypto_client_handshaker.cc
@@ -95,7 +95,7 @@
       proof_handler_(proof_handler),
       verify_ok_(false),
       stateless_reject_received_(false),
-      proof_verify_start_time_(QuicWallTime::Zero()),
+      proof_verify_start_time_(QuicTime::Zero()),
       num_scup_messages_received_(0),
       encryption_established_(false),
       handshake_confirmed_(false),
@@ -272,7 +272,7 @@
     // the proof.
     DCHECK(crypto_config_->proof_verifier());
     // Track proof verification time when cached server config is used.
-    proof_verify_start_time_ = session()->connection()->clock()->WallNow();
+    proof_verify_start_time_ = session()->connection()->clock()->Now();
     chlo_hash_ = cached->chlo_hash();
     // If the cached state needs to be verified, do it now.
     next_state_ = STATE_VERIFY_PROOF;
@@ -381,15 +381,15 @@
   SendHandshakeMessage(out);
   // Be prepared to decrypt with the new server write key.
   session()->connection()->SetAlternativeDecrypter(
-      ENCRYPTION_INITIAL,
+      ENCRYPTION_ZERO_RTT,
       std::move(crypto_negotiated_params_->initial_crypters.decrypter),
       true /* latch once used */);
   // Send subsequent packets under encryption on the assumption that the
   // server will accept the handshake.
   session()->connection()->SetEncrypter(
-      ENCRYPTION_INITIAL,
+      ENCRYPTION_ZERO_RTT,
       std::move(crypto_negotiated_params_->initial_crypters.encrypter));
-  session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+  session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
 
   // TODO(ianswett): Merge ENCRYPTION_REESTABLISHED and
   // ENCRYPTION_FIRST_ESTABLSIHED
@@ -498,12 +498,10 @@
 
 void QuicCryptoClientHandshaker::DoVerifyProofComplete(
     QuicCryptoClientConfig::CachedState* cached) {
-  if (!proof_verify_start_time_.IsZero()) {
+  if (proof_verify_start_time_.IsInitialized()) {
     QUIC_CLIENT_HISTOGRAM_TIMES(
         "QuicSession.VerifyProofTime.CachedServerConfig",
-        QuicTime::Delta::FromMicroseconds(
-            session()->connection()->clock()->WallNow().ToUNIXMicroseconds() -
-            proof_verify_start_time_.ToUNIXMicroseconds()),
+        (session()->connection()->clock()->Now() - proof_verify_start_time_),
         QuicTime::Delta::FromMilliseconds(1), QuicTime::Delta::FromSeconds(10),
         50, "");
   }
diff --git a/quic/core/quic_crypto_client_handshaker.h b/quic/core/quic_crypto_client_handshaker.h
index 14fe0a8..c84c2cd 100644
--- a/quic/core/quic_crypto_client_handshaker.h
+++ b/quic/core/quic_crypto_client_handshaker.h
@@ -222,7 +222,7 @@
   // STATE_VERIFY_PROOF*, and subsequent STATE_SEND_CHLO state.
   bool stateless_reject_received_;
 
-  QuicWallTime proof_verify_start_time_;
+  QuicTime proof_verify_start_time_;
 
   int num_scup_messages_received_;
 
diff --git a/quic/core/quic_crypto_handshaker.cc b/quic/core/quic_crypto_handshaker.cc
index dab2741..fa0f78a 100644
--- a/quic/core/quic_crypto_handshaker.cc
+++ b/quic/core/quic_crypto_handshaker.cc
@@ -26,8 +26,8 @@
   session()->OnCryptoHandshakeMessageSent(message);
   last_sent_handshake_message_tag_ = message.tag();
   const QuicData& data = message.GetSerialized();
-  stream_->WriteOrBufferData(QuicStringPiece(data.data(), data.length()), false,
-                             nullptr);
+  stream_->WriteCryptoData(session_->connection()->encryption_level(),
+                           data.AsStringPiece());
 }
 
 void QuicCryptoHandshaker::OnError(CryptoFramer* framer) {
diff --git a/quic/core/quic_crypto_server_handshaker.cc b/quic/core/quic_crypto_server_handshaker.cc
index e79a60c..08ef18b 100644
--- a/quic/core/quic_crypto_server_handshaker.cc
+++ b/quic/core/quic_crypto_server_handshaker.cc
@@ -225,13 +225,13 @@
   //
   // NOTE: the SHLO will be encrypted with the new server write key.
   session()->connection()->SetEncrypter(
-      ENCRYPTION_INITIAL,
+      ENCRYPTION_ZERO_RTT,
       std::move(crypto_negotiated_params_->initial_crypters.encrypter));
-  session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+  session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
   // Set the decrypter immediately so that we no longer accept unencrypted
   // packets.
   session()->connection()->SetDecrypter(
-      ENCRYPTION_INITIAL,
+      ENCRYPTION_ZERO_RTT,
       std::move(crypto_negotiated_params_->initial_crypters.decrypter));
   session()->connection()->SetDiversificationNonce(*diversification_nonce);
 
@@ -310,9 +310,13 @@
 
   QUIC_DVLOG(1) << "Server: Sending server config update: "
                 << message.DebugString();
-  const QuicData& data = message.GetSerialized();
-  stream_->WriteOrBufferData(QuicStringPiece(data.data(), data.length()), false,
-                             nullptr);
+  if (transport_version() < QUIC_VERSION_47) {
+    const QuicData& data = message.GetSerialized();
+    stream_->WriteOrBufferData(QuicStringPiece(data.data(), data.length()),
+                               false, nullptr);
+  } else {
+    SendHandshakeMessage(message);
+  }
 
   ++num_server_config_update_messages_sent_;
 }
@@ -352,6 +356,11 @@
 QuicLongHeaderType QuicCryptoServerHandshaker::GetLongHeaderType(
     QuicStreamOffset /*offset*/) const {
   if (last_sent_handshake_message_tag() == kSREJ) {
+    if (QuicVersionHasLongHeaderLengths(
+            session()->connection()->transport_version())) {
+      return HANDSHAKE;
+    }
+    // TODO(b/123493765): we should probably not be sending RETRY here.
     return RETRY;
   }
   if (last_sent_handshake_message_tag() == kSHLO) {
diff --git a/quic/core/quic_crypto_server_stream_test.cc b/quic/core/quic_crypto_server_stream_test.cc
index 33635d8..c0a3e08 100644
--- a/quic/core/quic_crypto_server_stream_test.cc
+++ b/quic/core/quic_crypto_server_stream_test.cc
@@ -189,7 +189,7 @@
   ParsedQuicVersionVector supported_versions_ = AllSupportedVersions();
 };
 
-INSTANTIATE_TEST_CASE_P(Tests, QuicCryptoServerStreamTest, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Tests, QuicCryptoServerStreamTest, testing::Bool());
 
 TEST_P(QuicCryptoServerStreamTest, NotInitiallyConected) {
   Initialize();
@@ -497,9 +497,9 @@
             std::unique_ptr<FailingProofSource>(new FailingProofSource)) {}
 };
 
-INSTANTIATE_TEST_CASE_P(MoreTests,
-                        QuicCryptoServerStreamTestWithFailingProofSource,
-                        testing::Bool());
+INSTANTIATE_TEST_SUITE_P(MoreTests,
+                         QuicCryptoServerStreamTestWithFailingProofSource,
+                         testing::Bool());
 
 TEST_P(QuicCryptoServerStreamTestWithFailingProofSource, Test) {
   Initialize();
@@ -531,9 +531,9 @@
   QuicCryptoServerConfigPeer crypto_config_peer_;
 };
 
-INSTANTIATE_TEST_CASE_P(YetMoreTests,
-                        QuicCryptoServerStreamTestWithFakeProofSource,
-                        testing::Bool());
+INSTANTIATE_TEST_SUITE_P(YetMoreTests,
+                         QuicCryptoServerStreamTestWithFakeProofSource,
+                         testing::Bool());
 
 // Regression test for b/35422225, in which multiple CHLOs arriving on the same
 // connection in close succession could cause a crash, especially when the use
diff --git a/quic/core/quic_crypto_stream.cc b/quic/core/quic_crypto_stream.cc
index 8199571..43d1f1a 100644
--- a/quic/core/quic_crypto_stream.cc
+++ b/quic/core/quic_crypto_stream.cc
@@ -27,7 +27,10 @@
                      session->connection()->transport_version()),
                  session,
                  /*is_static=*/true,
-                 BIDIRECTIONAL) {
+                 BIDIRECTIONAL),
+      substreams_{{this, ENCRYPTION_NONE},
+                  {this, ENCRYPTION_ZERO_RTT},
+                  {this, ENCRYPTION_FORWARD_SECURE}} {
   // The crypto stream is exempt from connection level flow control.
   DisableConnectionFlowControlForThisStream();
 }
@@ -43,56 +46,57 @@
       /*include_diversification_nonce=*/true,
       version > QUIC_VERSION_43 ? PACKET_4BYTE_PACKET_NUMBER
                                 : PACKET_1BYTE_PACKET_NUMBER,
+      VARIABLE_LENGTH_INTEGER_LENGTH_1, VARIABLE_LENGTH_INTEGER_LENGTH_2,
       /*offset=*/0);
 }
 
-void QuicCryptoStream::OnDataAvailable() {
-  struct iovec iov;
-  // When calling CryptoMessageParser::ProcessInput, an EncryptionLevel needs to
-  // be provided. Note that in the general case, the following code may be
-  // incorrect. When a stream frame is added to the sequencer, the encryption
-  // level provided by the connection will be the encryption level that the
-  // frame was received under, but stream frames can be received out of order.
-  // If a later stream frame at a higher encryption level is received before an
-  // earlier stream frame at a lower encryption level, this code will call
-  // CryptoMessageParser::Process input with the data from both frames, but
-  // indicate that they both were received at the higher encryption level.
-  //
-  // For QUIC crypto, this is not a problem, because the CryptoFramer (which
-  // implements CryptoMessageParser) ignores the EncryptionLevel passed into
-  // ProcessInput.
-  //
-  // For the TLS handshake, this does not cause an issue for the transition from
-  // initial encryption (ClientHello, HelloRetryRequest, and ServerHello) to
-  // handshake encryption, as all data from the initial encryption level is
-  // needed to derive the handshake encryption keys. For the transition from
-  // handshake encryption to 1-RTT application data encryption, all messages at
-  // the handshake encryption level *except* the client Finished are needed. The
-  // only place this logic would be broken is if a server receives a crypto
-  // handshake message that is encrypted under the 1-RTT data keys before
-  // receiving the client's Finished message (under handshake encryption keys).
-  // Right now, this implementation of TLS in QUIC does not support doing that,
-  // but it is possible (although unlikely) that other implementations could.
-  // Therefore, this needs to be fixed before the TLS handshake is enabled.
-  //
-  // TODO(nharper): Use a more robust and correct mechanism to provide the
-  // EncryptionLevel to CryptoMessageParser::ProcessInput. This must be done
-  // before enabling the TLS handshake.
+void QuicCryptoStream::OnCryptoFrame(const QuicCryptoFrame& frame) {
+  QUIC_BUG_IF(session()->connection()->transport_version() < QUIC_VERSION_47)
+      << "Versions less than 47 shouldn't receive CRYPTO frames";
   EncryptionLevel level = session()->connection()->last_decrypted_level();
-  while (sequencer()->GetReadableRegion(&iov)) {
+  substreams_[level].sequencer.OnCryptoFrame(frame);
+}
+
+void QuicCryptoStream::OnStreamFrame(const QuicStreamFrame& frame) {
+  if (session()->connection()->transport_version() >= QUIC_VERSION_47) {
+    QUIC_PEER_BUG
+        << "Crypto data received in stream frame instead of crypto frame";
+    CloseConnectionWithDetails(QUIC_INVALID_STREAM_DATA,
+                               "Unexpected stream frame");
+  }
+  QuicStream::OnStreamFrame(frame);
+}
+
+void QuicCryptoStream::OnDataAvailable() {
+  EncryptionLevel level = session()->connection()->last_decrypted_level();
+  if (session()->connection()->transport_version() < QUIC_VERSION_47) {
+    // Versions less than 47 only support QUIC crypto, which ignores the
+    // EncryptionLevel passed into CryptoMessageParser::ProcessInput (and
+    // OnDataAvailableInSequencer).
+    OnDataAvailableInSequencer(sequencer(), level);
+    return;
+  }
+  OnDataAvailableInSequencer(&substreams_[level].sequencer, level);
+}
+
+void QuicCryptoStream::OnDataAvailableInSequencer(
+    QuicStreamSequencer* sequencer,
+    EncryptionLevel level) {
+  struct iovec iov;
+  while (sequencer->GetReadableRegion(&iov)) {
     QuicStringPiece data(static_cast<char*>(iov.iov_base), iov.iov_len);
     if (!crypto_message_parser()->ProcessInput(data, level)) {
       CloseConnectionWithDetails(crypto_message_parser()->error(),
                                  crypto_message_parser()->error_detail());
       return;
     }
-    sequencer()->MarkConsumed(iov.iov_len);
+    sequencer->MarkConsumed(iov.iov_len);
     if (handshake_confirmed() &&
         crypto_message_parser()->InputBytesRemaining() == 0) {
       // If the handshake is complete and the current message has been fully
       // processed then no more handshake messages are likely to arrive soon
       // so release the memory in the stream sequencer.
-      sequencer()->ReleaseBufferIfEmpty();
+      sequencer->ReleaseBufferIfEmpty();
     }
   }
 }
@@ -113,33 +117,90 @@
 
 void QuicCryptoStream::WriteCryptoData(EncryptionLevel level,
                                        QuicStringPiece data) {
-  // TODO(nharper): This approach to writing data, by setting the encryption
-  // level, calling WriteOrBufferData, and then restoring the encryption level,
-  // is fragile and assumes that the data gets received by the peer when
-  // WriteOrBufferData is called. There is no guarantee that data will get
-  // retransmitted at the correct level. This needs to be redone with the
-  // cleanup for OnDataAvailable by managing the streams/crypto frames for
-  // encryption levels separately.
+  if (session()->connection()->transport_version() < QUIC_VERSION_47) {
+    // The QUIC crypto handshake takes care of setting the appropriate
+    // encryption level before writing data. Since that is the only handshake
+    // supported in versions less than 47, |level| can be ignored here.
+    WriteOrBufferData(data, /* fin */ false, /* ack_listener */ nullptr);
+    return;
+  }
+  if (data.empty()) {
+    QUIC_BUG << "Empty crypto data being written";
+    return;
+  }
+  // Append |data| to the send buffer for this encryption level.
+  struct iovec iov(QuicUtils::MakeIovec(data));
+  QuicStreamSendBuffer* send_buffer = &substreams_[level].send_buffer;
+  QuicStreamOffset offset = send_buffer->stream_offset();
+  send_buffer->SaveStreamData(&iov, /*iov_count=*/1, /*iov_offset=*/0,
+                              data.length());
+  if (kMaxStreamLength - offset < data.length()) {
+    QUIC_BUG << "Writing too much crypto handshake data";
+    // TODO(nharper): Switch this to an IETF QUIC error code, possibly
+    // INTERNAL_ERROR?
+    CloseConnectionWithDetails(QUIC_STREAM_LENGTH_OVERFLOW,
+                               "Writing too much crypto handshake data");
+  }
+
+  // Set long header type based on the encryption level.
+  if (level != ENCRYPTION_FORWARD_SECURE) {
+    QuicStreamOffset fake_offset = level == ENCRYPTION_NONE ? 0 : 1;
+    // Implementations of GetLongHeaderType either don't care at all about the
+    // offset, or only care whether or not it's 0. However, they do care that it
+    // is an absolute offset from the start of unencrypted crypto data, not the
+    // offset at a particular encryption level.
+    QuicLongHeaderType type = GetLongHeaderType(fake_offset);
+    session()->connection()->SetLongHeaderType(type);
+  }
   EncryptionLevel current_level = session()->connection()->encryption_level();
   session()->connection()->SetDefaultEncryptionLevel(level);
-  WriteOrBufferData(data, /* fin */ false, /* ack_listener */ nullptr);
-  if (current_level == ENCRYPTION_FORWARD_SECURE && level != current_level) {
-    session()->connection()->SetDefaultEncryptionLevel(current_level);
-  }
+  size_t bytes_consumed =
+      session()->connection()->SendCryptoData(level, data.length(), offset);
+  session()->connection()->SetDefaultEncryptionLevel(current_level);
+
+  send_buffer->OnStreamDataConsumed(bytes_consumed);
 }
 
 void QuicCryptoStream::OnSuccessfulVersionNegotiation(
     const ParsedQuicVersion& version) {}
 
+bool QuicCryptoStream::OnCryptoFrameAcked(const QuicCryptoFrame& frame,
+                                          QuicTime::Delta ack_delay_time) {
+  QuicByteCount newly_acked_length = 0;
+  if (!substreams_[frame.level].send_buffer.OnStreamDataAcked(
+          frame.offset, frame.data_length, &newly_acked_length)) {
+    CloseConnectionWithDetails(QUIC_INTERNAL_ERROR,
+                               "Trying to ack unsent crypto data.");
+    return false;
+  }
+  return newly_acked_length > 0;
+}
+
 void QuicCryptoStream::NeuterUnencryptedStreamData() {
-  for (const auto& interval : bytes_consumed_[ENCRYPTION_NONE]) {
+  if (session()->connection()->transport_version() < QUIC_VERSION_47) {
+    for (const auto& interval : bytes_consumed_[ENCRYPTION_NONE]) {
+      QuicByteCount newly_acked_length = 0;
+      send_buffer().OnStreamDataAcked(
+          interval.min(), interval.max() - interval.min(), &newly_acked_length);
+    }
+    return;
+  }
+  QuicStreamSendBuffer* send_buffer = &substreams_[ENCRYPTION_NONE].send_buffer;
+  // TODO(nharper): Consider adding a Clear() method to QuicStreamSendBuffer to
+  // replace the following code.
+  QuicIntervalSet<QuicStreamOffset> to_ack = send_buffer->bytes_acked();
+  to_ack.Complement(0, send_buffer->stream_offset());
+  for (const auto& interval : to_ack) {
     QuicByteCount newly_acked_length = 0;
-    send_buffer().OnStreamDataAcked(
+    send_buffer->OnStreamDataAcked(
         interval.min(), interval.max() - interval.min(), &newly_acked_length);
   }
 }
 
 void QuicCryptoStream::OnStreamDataConsumed(size_t bytes_consumed) {
+  if (session()->connection()->transport_version() >= QUIC_VERSION_47) {
+    QUIC_BUG << "Stream data consumed when CRYPTO frames should be in use";
+  }
   if (bytes_consumed > 0) {
     bytes_consumed_[session()->connection()->encryption_level()].Add(
         stream_bytes_written(), stream_bytes_written() + bytes_consumed);
@@ -147,6 +208,38 @@
   QuicStream::OnStreamDataConsumed(bytes_consumed);
 }
 
+bool QuicCryptoStream::HasPendingCryptoRetransmission() {
+  if (session()->connection()->transport_version() < QUIC_VERSION_47) {
+    return false;
+  }
+  for (EncryptionLevel level :
+       {ENCRYPTION_NONE, ENCRYPTION_ZERO_RTT, ENCRYPTION_FORWARD_SECURE}) {
+    if (substreams_[level].send_buffer.HasPendingRetransmission()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+void QuicCryptoStream::WritePendingCryptoRetransmission() {
+  QUIC_BUG_IF(session()->connection()->transport_version() < QUIC_VERSION_47)
+      << "Versions less than 47 don't write CRYPTO frames";
+  EncryptionLevel current_encryption_level =
+      session()->connection()->encryption_level();
+  for (EncryptionLevel level :
+       {ENCRYPTION_NONE, ENCRYPTION_ZERO_RTT, ENCRYPTION_FORWARD_SECURE}) {
+    QuicStreamSendBuffer* send_buffer = &substreams_[level].send_buffer;
+    session()->connection()->SetDefaultEncryptionLevel(level);
+    while (send_buffer->HasPendingRetransmission()) {
+      auto pending = send_buffer->NextPendingRetransmission();
+      size_t bytes_consumed = session()->connection()->SendCryptoData(
+          level, pending.length, pending.offset);
+      send_buffer->OnStreamDataRetransmitted(pending.offset, bytes_consumed);
+    }
+  }
+  session()->connection()->SetDefaultEncryptionLevel(current_encryption_level);
+}
+
 void QuicCryptoStream::WritePendingRetransmission() {
   while (HasPendingRetransmission()) {
     StreamPendingRetransmission pending =
@@ -236,5 +329,97 @@
   return true;
 }
 
+uint64_t QuicCryptoStream::crypto_bytes_read() const {
+  if (session()->connection()->transport_version() < QUIC_VERSION_47) {
+    return stream_bytes_read();
+  }
+  return substreams_[ENCRYPTION_NONE].sequencer.NumBytesConsumed() +
+         substreams_[ENCRYPTION_ZERO_RTT].sequencer.NumBytesConsumed() +
+         substreams_[ENCRYPTION_FORWARD_SECURE].sequencer.NumBytesConsumed();
+}
+
+uint64_t QuicCryptoStream::BytesReadOnLevel(EncryptionLevel level) const {
+  return substreams_[level].sequencer.NumBytesConsumed();
+}
+
+bool QuicCryptoStream::WriteCryptoFrame(EncryptionLevel level,
+                                        QuicStreamOffset offset,
+                                        QuicByteCount data_length,
+                                        QuicDataWriter* writer) {
+  QUIC_BUG_IF(session()->connection()->transport_version() < QUIC_VERSION_47)
+      << "Versions less than 47 don't write CRYPTO frames (2)";
+  return substreams_[level].send_buffer.WriteStreamData(offset, data_length,
+                                                        writer);
+}
+
+void QuicCryptoStream::OnCryptoFrameLost(QuicCryptoFrame* crypto_frame) {
+  QUIC_BUG_IF(session()->connection()->transport_version() < QUIC_VERSION_47)
+      << "Versions less than 47 don't lose CRYPTO frames";
+  substreams_[crypto_frame->level].send_buffer.OnStreamDataLost(
+      crypto_frame->offset, crypto_frame->data_length);
+}
+
+void QuicCryptoStream::RetransmitData(QuicCryptoFrame* crypto_frame) {
+  QUIC_BUG_IF(session()->connection()->transport_version() < QUIC_VERSION_47)
+      << "Versions less than 47 don't retransmit CRYPTO frames";
+  QuicIntervalSet<QuicStreamOffset> retransmission(
+      crypto_frame->offset, crypto_frame->offset + crypto_frame->data_length);
+  QuicStreamSendBuffer* send_buffer =
+      &substreams_[crypto_frame->level].send_buffer;
+  retransmission.Difference(send_buffer->bytes_acked());
+  if (retransmission.Empty()) {
+    return;
+  }
+  EncryptionLevel current_encryption_level =
+      session()->connection()->encryption_level();
+  for (const auto& interval : retransmission) {
+    size_t retransmission_offset = interval.min();
+    size_t retransmission_length = interval.max() - interval.min();
+    session()->connection()->SetDefaultEncryptionLevel(crypto_frame->level);
+    size_t bytes_consumed = session()->connection()->SendCryptoData(
+        crypto_frame->level, retransmission_length, retransmission_offset);
+    send_buffer->OnStreamDataRetransmitted(retransmission_offset,
+                                           bytes_consumed);
+  }
+  session()->connection()->SetDefaultEncryptionLevel(current_encryption_level);
+}
+
+bool QuicCryptoStream::IsFrameOutstanding(EncryptionLevel level,
+                                          size_t offset,
+                                          size_t length) const {
+  if (session()->connection()->transport_version() < QUIC_VERSION_47) {
+    // This only happens if a client was originally configured for a version
+    // greater than 45, but received a version negotiation packet and is
+    // attempting to retransmit for a version less than 47. Outside of tests,
+    // this is a misconfiguration of the client, and this connection will be
+    // doomed. Return false here to avoid trying to retransmit CRYPTO frames on
+    // the wrong transport version.
+    return false;
+  }
+  return substreams_[level].send_buffer.IsStreamDataOutstanding(offset, length);
+}
+
+bool QuicCryptoStream::IsWaitingForAcks() const {
+  if (session()->connection()->transport_version() < QUIC_VERSION_47) {
+    return QuicStream::IsWaitingForAcks();
+  }
+  for (EncryptionLevel level :
+       {ENCRYPTION_NONE, ENCRYPTION_ZERO_RTT, ENCRYPTION_FORWARD_SECURE}) {
+    if (substreams_[level].send_buffer.stream_bytes_outstanding()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+QuicCryptoStream::CryptoSubstream::CryptoSubstream(
+    QuicCryptoStream* crypto_stream,
+    EncryptionLevel)
+    : sequencer(crypto_stream),
+      send_buffer(crypto_stream->session()
+                      ->connection()
+                      ->helper()
+                      ->GetStreamSendBufferAllocator()) {}
+
 #undef ENDPOINT  // undef for jumbo builds
 }  // namespace quic
diff --git a/quic/core/quic_crypto_stream.h b/quic/core/quic_crypto_stream.h
index 0593f44..cfd81c7 100644
--- a/quic/core/quic_crypto_stream.h
+++ b/quic/core/quic_crypto_stream.h
@@ -45,8 +45,16 @@
       QuicTransportVersion version);
 
   // QuicStream implementation
+  void OnStreamFrame(const QuicStreamFrame& frame) override;
   void OnDataAvailable() override;
 
+  // Called when a CRYPTO frame is received.
+  void OnCryptoFrame(const QuicCryptoFrame& frame);
+
+  // Called when a CRYPTO frame is ACKed.
+  bool OnCryptoFrameAcked(const QuicCryptoFrame& frame,
+                          QuicTime::Delta ack_delay_time);
+
   // Performs key extraction to derive a new secret of |result_len| bytes
   // dependent on |label|, |context|, and the stream's negotiated subkey secret.
   // Returns false if the handshake has not been confirmed or the parameters are
@@ -86,6 +94,13 @@
   // Override to record the encryption level of consumed data.
   void OnStreamDataConsumed(size_t bytes_consumed) override;
 
+  // Returns whether there are any bytes pending retransmission in CRYPTO
+  // frames.
+  virtual bool HasPendingCryptoRetransmission();
+
+  // Writes any pending CRYPTO frame retransmissions.
+  void WritePendingCryptoRetransmission();
+
   // Override to retransmit lost crypto data with the appropriate encryption
   // level.
   void WritePendingRetransmission() override;
@@ -95,11 +110,66 @@
                             QuicByteCount data_length,
                             bool fin) override;
 
+  // Returns the number of bytes of handshake data that have been received from
+  // the peer in either CRYPTO or STREAM frames.
+  uint64_t crypto_bytes_read() const;
+
+  // Returns the number of bytes of handshake data that have been received from
+  // the peer in CRYPTO frames at a particular encryption level.
+  QuicByteCount BytesReadOnLevel(EncryptionLevel level) const;
+
+  // Writes |data_length| of data of a crypto frame to |writer|. The data
+  // written is from the send buffer for encryption level |level| and starts at
+  // |offset|.
+  bool WriteCryptoFrame(EncryptionLevel level,
+                        QuicStreamOffset offset,
+                        QuicByteCount data_length,
+                        QuicDataWriter* writer);
+
+  // Called when data from a CRYPTO frame is considered lost. The lost data is
+  // identified by the encryption level, offset, and length in |crypto_frame|.
+  void OnCryptoFrameLost(QuicCryptoFrame* crypto_frame);
+
+  // Called to retransmit any outstanding data in the range indicated by the
+  // encryption level, offset, and length in |crypto_frame|.
+  void RetransmitData(QuicCryptoFrame* crypto_frame);
+
+  // Returns true if any portion of the data at encryption level |level|
+  // starting at |offset| for |length| bytes is outstanding.
+  bool IsFrameOutstanding(EncryptionLevel level,
+                          size_t offset,
+                          size_t length) const;
+
+  // Returns true if the crypto handshake is still waiting for acks of sent
+  // data, and false if all data has been acked.
+  bool IsWaitingForAcks() const;
+
  private:
+  // Data sent and received in CRYPTO frames is sent at multiple encryption
+  // levels. Some of the state for the single logical crypto stream is split
+  // across encryption levels, and a CryptoSubstream is used to manage that
+  // state for a particular encryption level.
+  struct CryptoSubstream {
+    CryptoSubstream(QuicCryptoStream* crypto_stream, EncryptionLevel);
+
+    QuicStreamSequencer sequencer;
+    QuicStreamSendBuffer send_buffer;
+  };
+
+  // Helper method for OnDataAvailable. Calls CryptoMessageParser::ProcessInput
+  // with the data available in |sequencer| and |level|, and marks the data
+  // passed to ProcessInput as consumed.
+  void OnDataAvailableInSequencer(QuicStreamSequencer* sequencer,
+                                  EncryptionLevel level);
+
   // Consumed data according to encryption levels.
   // TODO(fayang): This is not needed once switching from QUIC crypto to
   // TLS 1.3, which never encrypts crypto data.
   QuicIntervalSet<QuicStreamOffset> bytes_consumed_[NUM_ENCRYPTION_LEVELS];
+
+  // Keeps state for data sent/received in CRYPTO frames at each encryption
+  // level.
+  CryptoSubstream substreams_[NUM_ENCRYPTION_LEVELS];
 };
 
 }  // namespace quic
diff --git a/quic/core/quic_crypto_stream_test.cc b/quic/core/quic_crypto_stream_test.cc
index 6ce0b41..eb1a672 100644
--- a/quic/core/quic_crypto_stream_test.cc
+++ b/quic/core/quic_crypto_stream_test.cc
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
 #include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
@@ -104,10 +105,15 @@
 }
 
 TEST_F(QuicCryptoStreamTest, ProcessRawData) {
-  stream_->OnStreamFrame(QuicStreamFrame(
-      QuicUtils::GetCryptoStreamId(connection_->transport_version()),
-      /*fin=*/false,
-      /*offset=*/0, message_data_->AsStringPiece()));
+  if (connection_->transport_version() < QUIC_VERSION_47) {
+    stream_->OnStreamFrame(QuicStreamFrame(
+        QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+        /*fin=*/false,
+        /*offset=*/0, message_data_->AsStringPiece()));
+  } else {
+    stream_->OnCryptoFrame(QuicCryptoFrame(ENCRYPTION_NONE, /*offset*/ 0,
+                                           message_data_->AsStringPiece()));
+  }
   ASSERT_EQ(1u, stream_->messages()->size());
   const CryptoHandshakeMessage& message = (*stream_->messages())[0];
   EXPECT_EQ(kSHLO, message.tag());
@@ -126,9 +132,13 @@
 
   EXPECT_CALL(*connection_, CloseConnection(QUIC_CRYPTO_TAGS_OUT_OF_ORDER,
                                             testing::_, testing::_));
-  stream_->OnStreamFrame(QuicStreamFrame(
-      QuicUtils::GetCryptoStreamId(connection_->transport_version()),
-      /*fin=*/false, /*offset=*/0, bad));
+  if (connection_->transport_version() < QUIC_VERSION_47) {
+    stream_->OnStreamFrame(QuicStreamFrame(
+        QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+        /*fin=*/false, /*offset=*/0, bad));
+  } else {
+    stream_->OnCryptoFrame(QuicCryptoFrame(ENCRYPTION_NONE, /*offset*/ 0, bad));
+  }
 }
 
 TEST_F(QuicCryptoStreamTest, NoConnectionLevelFlowControl) {
@@ -137,6 +147,9 @@
 }
 
 TEST_F(QuicCryptoStreamTest, RetransmitCryptoData) {
+  if (connection_->transport_version() >= QUIC_VERSION_47) {
+    return;
+  }
   InSequence s;
   // Send [0, 1350) in ENCRYPTION_NONE.
   EXPECT_EQ(ENCRYPTION_NONE, connection_->encryption_level());
@@ -148,9 +161,9 @@
                  1350, 0, _))
       .WillOnce(Invoke(MockQuicSession::ConsumeData));
   stream_->WriteOrBufferData(data, false, nullptr);
-  // Send [1350, 2700) in ENCRYPTION_INITIAL.
-  connection_->SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
-  EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level());
+  // Send [1350, 2700) in ENCRYPTION_ZERO_RTT.
+  connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+  EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level());
   EXPECT_CALL(
       session_,
       WritevData(_,
@@ -192,7 +205,62 @@
   EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level());
 }
 
+TEST_F(QuicCryptoStreamTest, RetransmitCryptoDataInCryptoFrames) {
+  if (connection_->transport_version() < QUIC_VERSION_47) {
+    return;
+  }
+  EXPECT_CALL(*connection_, SendCryptoData(_, _, _)).Times(0);
+  InSequence s;
+  // Send [0, 1350) in ENCRYPTION_NONE.
+  EXPECT_EQ(ENCRYPTION_NONE, connection_->encryption_level());
+  QuicString data(1350, 'a');
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_NONE, 1350, 0))
+      .WillOnce(Invoke(connection_,
+                       &MockQuicConnection::QuicConnection_SendCryptoData));
+  stream_->WriteCryptoData(ENCRYPTION_NONE, data);
+  // Send [1350, 2700) in ENCRYPTION_ZERO_RTT.
+  connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+  std::unique_ptr<NullEncrypter> encrypter =
+      QuicMakeUnique<NullEncrypter>(Perspective::IS_CLIENT);
+  connection_->SetEncrypter(ENCRYPTION_ZERO_RTT, std::move(encrypter));
+  EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level());
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 1350, 0))
+      .WillOnce(Invoke(connection_,
+                       &MockQuicConnection::QuicConnection_SendCryptoData));
+  stream_->WriteCryptoData(ENCRYPTION_ZERO_RTT, data);
+  connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+  EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level());
+
+  // Lost [0, 1000).
+  QuicCryptoFrame lost_frame(ENCRYPTION_NONE, 0, 1000);
+  stream_->OnCryptoFrameLost(&lost_frame);
+  EXPECT_TRUE(stream_->HasPendingCryptoRetransmission());
+  // Lost [1200, 2000).
+  lost_frame = QuicCryptoFrame(ENCRYPTION_NONE, 1200, 150);
+  stream_->OnCryptoFrameLost(&lost_frame);
+  lost_frame = QuicCryptoFrame(ENCRYPTION_ZERO_RTT, 0, 650);
+  stream_->OnCryptoFrameLost(&lost_frame);
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_NONE, 1000, 0))
+      .WillOnce(Invoke(connection_,
+                       &MockQuicConnection::QuicConnection_SendCryptoData));
+  // Verify [1200, 2000) are sent in [1200, 1350) and [1350, 2000) because of
+  // they are in different encryption levels.
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_NONE, 150, 1200))
+      .WillOnce(Invoke(connection_,
+                       &MockQuicConnection::QuicConnection_SendCryptoData));
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 650, 0))
+      .WillOnce(Invoke(connection_,
+                       &MockQuicConnection::QuicConnection_SendCryptoData));
+  stream_->WritePendingCryptoRetransmission();
+  EXPECT_FALSE(stream_->HasPendingCryptoRetransmission());
+  // Verify connection's encryption level has restored.
+  EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level());
+}
+
 TEST_F(QuicCryptoStreamTest, NeuterUnencryptedStreamData) {
+  if (connection_->transport_version() >= QUIC_VERSION_47) {
+    return;
+  }
   // Send [0, 1350) in ENCRYPTION_NONE.
   EXPECT_EQ(ENCRYPTION_NONE, connection_->encryption_level());
   QuicString data(1350, 'a');
@@ -203,9 +271,9 @@
                  1350, 0, _))
       .WillOnce(Invoke(MockQuicSession::ConsumeData));
   stream_->WriteOrBufferData(data, false, nullptr);
-  // Send [1350, 2700) in ENCRYPTION_INITIAL.
-  connection_->SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
-  EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level());
+  // Send [1350, 2700) in ENCRYPTION_ZERO_RTT.
+  connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+  EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level());
   EXPECT_CALL(
       session_,
       WritevData(_,
@@ -231,7 +299,53 @@
   EXPECT_TRUE(stream_->HasPendingRetransmission());
 }
 
+TEST_F(QuicCryptoStreamTest, NeuterUnencryptedCryptoData) {
+  if (connection_->transport_version() < QUIC_VERSION_47) {
+    return;
+  }
+  // Send [0, 1350) in ENCRYPTION_NONE.
+  EXPECT_EQ(ENCRYPTION_NONE, connection_->encryption_level());
+  QuicString data(1350, 'a');
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_NONE, 1350, 0))
+      .WillOnce(Invoke(connection_,
+                       &MockQuicConnection::QuicConnection_SendCryptoData));
+  stream_->WriteCryptoData(ENCRYPTION_NONE, data);
+  // Send [1350, 2700) in ENCRYPTION_ZERO_RTT.
+  connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+  std::unique_ptr<NullEncrypter> encrypter =
+      QuicMakeUnique<NullEncrypter>(Perspective::IS_CLIENT);
+  connection_->SetEncrypter(ENCRYPTION_ZERO_RTT, std::move(encrypter));
+  EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level());
+  EXPECT_CALL(*connection_, SendCryptoData(_, _, _)).Times(0);
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 1350, 0))
+      .WillOnce(Invoke(connection_,
+                       &MockQuicConnection::QuicConnection_SendCryptoData));
+  stream_->WriteCryptoData(ENCRYPTION_ZERO_RTT, data);
+
+  // Lost [0, 1350).
+  QuicCryptoFrame lost_frame(ENCRYPTION_NONE, 0, 1350);
+  stream_->OnCryptoFrameLost(&lost_frame);
+  EXPECT_TRUE(stream_->HasPendingCryptoRetransmission());
+  // Neuters [0, 1350).
+  stream_->NeuterUnencryptedStreamData();
+  EXPECT_FALSE(stream_->HasPendingCryptoRetransmission());
+  // Lost [0, 1350) again.
+  stream_->OnCryptoFrameLost(&lost_frame);
+  EXPECT_FALSE(stream_->HasPendingCryptoRetransmission());
+
+  // Lost [1350, 2000), which starts at offset 0 at the ENCRYPTION_ZERO_RTT
+  // level.
+  lost_frame = QuicCryptoFrame(ENCRYPTION_ZERO_RTT, 0, 650);
+  stream_->OnCryptoFrameLost(&lost_frame);
+  EXPECT_TRUE(stream_->HasPendingCryptoRetransmission());
+  stream_->NeuterUnencryptedStreamData();
+  EXPECT_TRUE(stream_->HasPendingCryptoRetransmission());
+}
+
 TEST_F(QuicCryptoStreamTest, RetransmitStreamData) {
+  if (connection_->transport_version() >= QUIC_VERSION_47) {
+    return;
+  }
   InSequence s;
   // Send [0, 1350) in ENCRYPTION_NONE.
   EXPECT_EQ(ENCRYPTION_NONE, connection_->encryption_level());
@@ -243,9 +357,9 @@
                  1350, 0, _))
       .WillOnce(Invoke(MockQuicSession::ConsumeData));
   stream_->WriteOrBufferData(data, false, nullptr);
-  // Send [1350, 2700) in ENCRYPTION_INITIAL.
-  connection_->SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
-  EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level());
+  // Send [1350, 2700) in ENCRYPTION_ZERO_RTT.
+  connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+  EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level());
   EXPECT_CALL(
       session_,
       WritevData(_,
@@ -257,7 +371,10 @@
   EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level());
 
   // Ack [2000, 2500).
-  stream_->OnStreamFrameAcked(2000, 500, false, QuicTime::Delta::Zero());
+  QuicByteCount newly_acked_length = 0;
+  stream_->OnStreamFrameAcked(2000, 500, false, QuicTime::Delta::Zero(),
+                              &newly_acked_length);
+  EXPECT_EQ(500u, newly_acked_length);
 
   // Force crypto stream to send [1350, 2700) and only [1350, 1500) is consumed.
   EXPECT_CALL(
@@ -298,8 +415,69 @@
   EXPECT_TRUE(stream_->RetransmitStreamData(0, 0, false));
 }
 
+TEST_F(QuicCryptoStreamTest, RetransmitStreamDataWithCryptoFrames) {
+  if (connection_->transport_version() < QUIC_VERSION_47) {
+    return;
+  }
+  InSequence s;
+  // Send [0, 1350) in ENCRYPTION_NONE.
+  EXPECT_EQ(ENCRYPTION_NONE, connection_->encryption_level());
+  QuicString data(1350, 'a');
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_NONE, 1350, 0))
+      .WillOnce(Invoke(connection_,
+                       &MockQuicConnection::QuicConnection_SendCryptoData));
+  stream_->WriteCryptoData(ENCRYPTION_NONE, data);
+  // Send [1350, 2700) in ENCRYPTION_ZERO_RTT.
+  connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+  std::unique_ptr<NullEncrypter> encrypter =
+      QuicMakeUnique<NullEncrypter>(Perspective::IS_CLIENT);
+  connection_->SetEncrypter(ENCRYPTION_ZERO_RTT, std::move(encrypter));
+  EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level());
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 1350, 0))
+      .WillOnce(Invoke(connection_,
+                       &MockQuicConnection::QuicConnection_SendCryptoData));
+  stream_->WriteCryptoData(ENCRYPTION_ZERO_RTT, data);
+  connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+  EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level());
+
+  // Ack [2000, 2500).
+  QuicCryptoFrame acked_frame(ENCRYPTION_ZERO_RTT, 650, 500);
+  EXPECT_TRUE(
+      stream_->OnCryptoFrameAcked(acked_frame, QuicTime::Delta::Zero()));
+
+  // Retransmit only [1350, 1500).
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 150, 0))
+      .WillOnce(Invoke(connection_,
+                       &MockQuicConnection::QuicConnection_SendCryptoData));
+  QuicCryptoFrame frame_to_retransmit(ENCRYPTION_ZERO_RTT, 0, 150);
+  stream_->RetransmitData(&frame_to_retransmit);
+
+  // Verify connection's encryption level has restored.
+  EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level());
+
+  // Retransmit [1350, 2700) again and all data is sent.
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 650, 0))
+      .WillOnce(Invoke(connection_,
+                       &MockQuicConnection::QuicConnection_SendCryptoData));
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 200, 1150))
+      .WillOnce(Invoke(connection_,
+                       &MockQuicConnection::QuicConnection_SendCryptoData));
+  frame_to_retransmit = QuicCryptoFrame(ENCRYPTION_ZERO_RTT, 0, 1350);
+  stream_->RetransmitData(&frame_to_retransmit);
+  // Verify connection's encryption level has restored.
+  EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level());
+
+  EXPECT_CALL(*connection_, SendCryptoData(_, _, _)).Times(0);
+  // Force to send an empty frame.
+  QuicCryptoFrame empty_frame(ENCRYPTION_FORWARD_SECURE, 0, 0);
+  stream_->RetransmitData(&empty_frame);
+}
+
 // Regression test for b/115926584.
 TEST_F(QuicCryptoStreamTest, HasUnackedCryptoData) {
+  if (connection_->transport_version() >= QUIC_VERSION_47) {
+    return;
+  }
   QuicString data(1350, 'a');
   EXPECT_CALL(
       session_,
@@ -325,6 +503,21 @@
   EXPECT_TRUE(session_.HasUnackedCryptoData());
 }
 
+TEST_F(QuicCryptoStreamTest, HasUnackedCryptoDataWithCryptoFrames) {
+  if (connection_->transport_version() < QUIC_VERSION_47) {
+    return;
+  }
+  // Send [0, 1350) in ENCRYPTION_NONE.
+  EXPECT_EQ(ENCRYPTION_NONE, connection_->encryption_level());
+  QuicString data(1350, 'a');
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_NONE, 1350, 0))
+      .WillOnce(Invoke(connection_,
+                       &MockQuicConnection::QuicConnection_SendCryptoData));
+  stream_->WriteCryptoData(ENCRYPTION_NONE, data);
+  EXPECT_TRUE(stream_->IsWaitingForAcks());
+  EXPECT_TRUE(session_.HasUnackedCryptoData());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/quic_data_reader.cc b/quic/core/quic_data_reader.cc
index 13a4c5a..191c755 100644
--- a/quic/core/quic_data_reader.cc
+++ b/quic/core/quic_data_reader.cc
@@ -12,6 +12,9 @@
 
 namespace quic {
 
+QuicDataReader::QuicDataReader(const char* data, const size_t len)
+    : QuicDataReader(data, len, NETWORK_BYTE_ORDER) {}
+
 QuicDataReader::QuicDataReader(const char* data,
                                const size_t len,
                                Endianness endianness)
@@ -129,17 +132,7 @@
 }
 
 bool QuicDataReader::ReadConnectionId(QuicConnectionId* connection_id,
-                                      uint8_t length,
-                                      Perspective perspective) {
-  if (!QuicConnectionIdSupportsVariableLength(perspective)) {
-    uint64_t connection_id64 = 0;
-    if (!ReadBytes(&connection_id64, sizeof(connection_id64))) {
-      return false;
-    }
-    *connection_id =
-        QuicConnectionIdFromUInt64(QuicEndian::NetToHost64(connection_id64));
-    return true;
-  }
+                                      uint8_t length) {
   DCHECK_LE(length, kQuicMaxConnectionIdLength);
 
   const bool ok = ReadBytes(connection_id->mutable_data(), length);
@@ -159,7 +152,7 @@
   return payload;
 }
 
-QuicStringPiece QuicDataReader::PeekRemainingPayload() {
+QuicStringPiece QuicDataReader::PeekRemainingPayload() const {
   return QuicStringPiece(data_ + pos_, len_ - pos_);
 }
 
@@ -183,20 +176,29 @@
   return len_ == pos_;
 }
 
-int QuicDataReader::PeekVarInt62Length() {
+QuicVariableLengthIntegerLength QuicDataReader::PeekVarInt62Length() {
   DCHECK_EQ(endianness_, NETWORK_BYTE_ORDER);
   const unsigned char* next =
       reinterpret_cast<const unsigned char*>(data_ + pos_);
   if (BytesRemaining() == 0) {
-    return 0;
+    return VARIABLE_LENGTH_INTEGER_LENGTH_0;
   }
-  return 1 << ((*next & 0b11000000) >> 6);
+  return static_cast<QuicVariableLengthIntegerLength>(
+      1 << ((*next & 0b11000000) >> 6));
 }
 
 size_t QuicDataReader::BytesRemaining() const {
   return len_ - pos_;
 }
 
+bool QuicDataReader::TruncateRemaining(size_t truncation_length) {
+  if (truncation_length > BytesRemaining()) {
+    return false;
+  }
+  len_ = pos_ + truncation_length;
+  return true;
+}
+
 bool QuicDataReader::CanRead(size_t bytes) const {
   return bytes <= (len_ - pos_);
 }
diff --git a/quic/core/quic_data_reader.h b/quic/core/quic_data_reader.h
index ca9f323..fff0ffd 100644
--- a/quic/core/quic_data_reader.h
+++ b/quic/core/quic_data_reader.h
@@ -32,6 +32,10 @@
 // called after failure, as they will also fail immediately.
 class QUIC_EXPORT_PRIVATE QuicDataReader {
  public:
+  // Constructs a reader using NETWORK_BYTE_ORDER endianness.
+  // Caller must provide an underlying buffer to work on.
+  QuicDataReader(const char* data, const size_t len);
+  // Constructs a reader using the specified endianness.
   // Caller must provide an underlying buffer to work on.
   QuicDataReader(const char* data, const size_t len, Endianness endianness);
   QuicDataReader(const QuicDataReader&) = delete;
@@ -75,11 +79,7 @@
   // Reads connection ID into the given output parameter.
   // Forwards the internal iterator on success.
   // Returns true on success, false otherwise.
-  // TODO(dschinazi) b/120240679 - remove perspective once these flags are
-  // deprecated: quic_variable_length_connection_ids_(client|server).
-  bool ReadConnectionId(QuicConnectionId* connection_id,
-                        uint8_t length,
-                        Perspective perspective);
+  bool ReadConnectionId(QuicConnectionId* connection_id, uint8_t length);
 
   // Reads tag represented as 32-bit unsigned integer into given output
   // parameter. Tags are in big endian on the wire (e.g., CHLO is
@@ -101,7 +101,7 @@
   // This should be kept in mind when handling memory management!
   //
   // DOES NOT forward the internal iterator.
-  QuicStringPiece PeekRemainingPayload();
+  QuicStringPiece PeekRemainingPayload() const;
 
   // Reads a given number of bytes into the given buffer. The buffer
   // must be of adequate size.
@@ -115,11 +115,17 @@
 
   // Returns the length in bytes of a variable length integer based on the next
   // two bits available. Returns 1, 2, 4, or 8 on success, and 0 on failure.
-  int PeekVarInt62Length();
+  QuicVariableLengthIntegerLength PeekVarInt62Length();
 
   // Returns the number of bytes remaining to be read.
   size_t BytesRemaining() const;
 
+  // Truncates the reader down by reducing its internal length.
+  // If called immediately after calling this, BytesRemaining will
+  // return |truncation_length|. If truncation_length is less than the
+  // current value of BytesRemaining, this does nothing and returns false.
+  bool TruncateRemaining(size_t truncation_length);
+
   // Returns the next byte that to be read. Must not be called when there are no
   // bytes to be read.
   //
@@ -158,7 +164,7 @@
   const char* data_;
 
   // The length of the data buffer that we're reading from.
-  const size_t len_;
+  size_t len_;
 
   // The location of the next read from our data buffer.
   size_t pos_;
diff --git a/quic/core/quic_data_writer.cc b/quic/core/quic_data_writer.cc
index 865b262..c7bedb5 100644
--- a/quic/core/quic_data_writer.cc
+++ b/quic/core/quic_data_writer.cc
@@ -15,6 +15,9 @@
 
 namespace quic {
 
+QuicDataWriter::QuicDataWriter(size_t size, char* buffer)
+    : QuicDataWriter(size, buffer, NETWORK_BYTE_ORDER) {}
+
 QuicDataWriter::QuicDataWriter(size_t size, char* buffer, Endianness endianness)
     : buffer_(buffer), capacity_(size), length_(0), endianness_(endianness) {}
 
@@ -171,14 +174,7 @@
   return WriteRepeatedByte(0x00, count);
 }
 
-bool QuicDataWriter::WriteConnectionId(QuicConnectionId connection_id,
-                                       Perspective perspective) {
-  if (!QuicConnectionIdSupportsVariableLength(perspective)) {
-    uint64_t connection_id64 =
-        QuicEndian::HostToNet64(QuicConnectionIdToUInt64(connection_id));
-
-    return WriteBytes(&connection_id64, sizeof(connection_id64));
-  }
+bool QuicDataWriter::WriteConnectionId(QuicConnectionId connection_id) {
   return WriteBytes(connection_id.data(), connection_id.length());
 }
 
@@ -283,23 +279,58 @@
   return false;
 }
 
+bool QuicDataWriter::WriteVarInt62(
+    uint64_t value,
+    QuicVariableLengthIntegerLength write_length) {
+  DCHECK_EQ(endianness_, NETWORK_BYTE_ORDER);
+
+  size_t remaining = capacity_ - length_;
+  if (remaining < write_length) {
+    return false;
+  }
+
+  const QuicVariableLengthIntegerLength min_length = GetVarInt62Len(value);
+  if (write_length < min_length) {
+    QUIC_BUG << "Cannot write value " << value << " with write_length "
+             << write_length;
+    return false;
+  }
+  if (write_length == min_length) {
+    return WriteVarInt62(value);
+  }
+
+  if (write_length == VARIABLE_LENGTH_INTEGER_LENGTH_2) {
+    return WriteUInt8(0b01000000) && WriteUInt8(value);
+  }
+  if (write_length == VARIABLE_LENGTH_INTEGER_LENGTH_4) {
+    return WriteUInt8(0b10000000) && WriteUInt8(0) && WriteUInt16(value);
+  }
+  if (write_length == VARIABLE_LENGTH_INTEGER_LENGTH_8) {
+    return WriteUInt8(0b11000000) && WriteUInt8(0) && WriteUInt16(0) &&
+           WriteUInt32(value);
+  }
+
+  QUIC_BUG << "Invalid write_length " << static_cast<int>(write_length);
+  return false;
+}
+
 // static
-int QuicDataWriter::GetVarInt62Len(uint64_t value) {
+QuicVariableLengthIntegerLength QuicDataWriter::GetVarInt62Len(uint64_t value) {
   if ((value & kVarInt62ErrorMask) != 0) {
     QUIC_BUG << "Attempted to encode a value, " << value
              << ", that is too big for VarInt62";
-    return 0;
+    return VARIABLE_LENGTH_INTEGER_LENGTH_0;
   }
   if ((value & kVarInt62Mask8Bytes) != 0) {
-    return 8;
+    return VARIABLE_LENGTH_INTEGER_LENGTH_8;
   }
   if ((value & kVarInt62Mask4Bytes) != 0) {
-    return 4;
+    return VARIABLE_LENGTH_INTEGER_LENGTH_4;
   }
   if ((value & kVarInt62Mask2Bytes) != 0) {
-    return 2;
+    return VARIABLE_LENGTH_INTEGER_LENGTH_2;
   }
-  return 1;
+  return VARIABLE_LENGTH_INTEGER_LENGTH_1;
 }
 
 bool QuicDataWriter::WriteStringPieceVarInt62(
diff --git a/quic/core/quic_data_writer.h b/quic/core/quic_data_writer.h
index 9423553..e107fcb 100644
--- a/quic/core/quic_data_writer.h
+++ b/quic/core/quic_data_writer.h
@@ -38,7 +38,11 @@
 // of the QuicDataWriter.
 class QUIC_EXPORT_PRIVATE QuicDataWriter {
  public:
-  // Creates a QuicDataWriter where |buffer| is not owned.
+  // Creates a QuicDataWriter where |buffer| is not owned
+  // using NETWORK_BYTE_ORDER endianness.
+  QuicDataWriter(size_t size, char* buffer);
+  // Creates a QuicDataWriter where |buffer| is not owned
+  // using the specified endianness.
   QuicDataWriter(size_t size, char* buffer, Endianness endianness);
   QuicDataWriter(const QuicDataWriter&) = delete;
   QuicDataWriter& operator=(const QuicDataWriter&) = delete;
@@ -68,6 +72,13 @@
   // buffer.
   bool WriteVarInt62(uint64_t value);
 
+  // Same as WriteVarInt62(uint64_t), but forces an encoding size to write to.
+  // This is not as optimized as WriteVarInt62(uint64_t).
+  // Returns false if the value does not fit in the specified write_length or if
+  // there is no room in the buffer.
+  bool WriteVarInt62(uint64_t value,
+                     QuicVariableLengthIntegerLength write_length);
+
   // Writes a string piece as a consecutive length/content pair. The
   // length is VarInt62 encoded.
   bool WriteStringPieceVarInt62(const QuicStringPiece& string_piece);
@@ -76,7 +87,7 @@
   // the given value using IETF VarInt62 encoding. Returns the number
   // of bytes required to encode the given integer or 0 if the value
   // is too large to encode.
-  static int GetVarInt62Len(uint64_t value);
+  static QuicVariableLengthIntegerLength GetVarInt62Len(uint64_t value);
 
   // Writes least significant |num_bytes| of a 64-bit unsigned integer in the
   // correct byte order.
@@ -96,10 +107,7 @@
   bool WritePaddingBytes(size_t count);
 
   // Write connection ID to the payload.
-  // TODO(dschinazi) b/120240679 - remove perspective once these flags are
-  // deprecated: quic_variable_length_connection_ids_(client|server).
-  bool WriteConnectionId(QuicConnectionId connection_id,
-                         Perspective perspective);
+  bool WriteConnectionId(QuicConnectionId connection_id);
 
   // Write tag as a 32-bit unsigned integer to the payload. As tags are already
   // converted to big endian (e.g., CHLO is 'C','H','L','O') in memory by TAG or
diff --git a/quic/core/quic_data_writer_test.cc b/quic/core/quic_data_writer_test.cc
index 9fe05e6..978143e 100644
--- a/quic/core/quic_data_writer_test.cc
+++ b/quic/core/quic_data_writer_test.cc
@@ -37,9 +37,8 @@
 
 class QuicDataWriterTest : public QuicTestWithParam<TestParams> {};
 
-INSTANTIATE_TEST_CASE_P(QuicDataWriterTests,
-                        QuicDataWriterTest,
-                        ::testing::ValuesIn(GetTestParams()));
+INSTANTIATE_TEST_SUITE_P(QuicDataWriterTests, QuicDataWriterTest,
+                         ::testing::ValuesIn(GetTestParams()));
 
 TEST_P(QuicDataWriterTest, SanityCheckUFloat16Consts) {
   // Check the arithmetic on the constants - otherwise the values below make
@@ -256,99 +255,16 @@
   ASSERT_LE(connection_id.length(), kQuicMaxConnectionIdLength);
   char buffer[kQuicMaxConnectionIdLength];
   QuicDataWriter writer(connection_id.length(), buffer, GetParam().endianness);
-  EXPECT_TRUE(writer.WriteConnectionId(connection_id, Perspective::IS_CLIENT));
+  EXPECT_TRUE(writer.WriteConnectionId(connection_id));
   test::CompareCharArraysWithHexError("connection_id", buffer,
                                       connection_id.length(), big_endian,
                                       connection_id.length());
 
   QuicConnectionId read_connection_id;
   QuicDataReader reader(buffer, connection_id.length(), GetParam().endianness);
-  EXPECT_TRUE(reader.ReadConnectionId(
-      &read_connection_id, QUIC_ARRAYSIZE(big_endian), Perspective::IS_CLIENT));
+  EXPECT_TRUE(
+      reader.ReadConnectionId(&read_connection_id, QUIC_ARRAYSIZE(big_endian)));
   EXPECT_EQ(connection_id, read_connection_id);
-
-  // TODO(dschinazi) b/120240679 - remove this second read once these flags are
-  // deprecated: quic_variable_length_connection_ids_(client|server).
-  QuicConnectionId read_connection_id2;
-  QuicDataReader reader2(buffer, connection_id.length(), GetParam().endianness);
-  EXPECT_TRUE(reader2.ReadConnectionId(&read_connection_id2,
-                                       QUIC_ARRAYSIZE(big_endian),
-                                       Perspective::IS_SERVER));
-  EXPECT_EQ(connection_id, read_connection_id2);
-}
-
-// TODO(dschinazi) b/120240679 - remove this test once these flags are
-// deprecated: quic_variable_length_connection_ids_(client|server).
-TEST_P(QuicDataWriterTest, WriteConnectionIdServerAllowingVariableLength) {
-  if (!GetQuicRestartFlag(quic_connection_ids_network_byte_order)) {
-    // This test is pointless if the flag is off.
-    return;
-  }
-  SetQuicRestartFlag(quic_variable_length_connection_ids_client, false);
-  SetQuicRestartFlag(quic_variable_length_connection_ids_server, true);
-  QuicConnectionId connection_id =
-      TestConnectionId(UINT64_C(0x0011223344556677));
-  char big_endian[] = {
-      0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
-  };
-  EXPECT_EQ(connection_id.length(), QUIC_ARRAYSIZE(big_endian));
-  ASSERT_LE(connection_id.length(), kQuicMaxConnectionIdLength);
-  char buffer[kQuicMaxConnectionIdLength];
-  QuicDataWriter writer(connection_id.length(), buffer, GetParam().endianness);
-  EXPECT_TRUE(writer.WriteConnectionId(connection_id, Perspective::IS_SERVER));
-  test::CompareCharArraysWithHexError("connection_id", buffer,
-                                      connection_id.length(), big_endian,
-                                      connection_id.length());
-
-  QuicConnectionId read_connection_id;
-  QuicDataReader reader(buffer, connection_id.length(), GetParam().endianness);
-  EXPECT_TRUE(reader.ReadConnectionId(
-      &read_connection_id, QUIC_ARRAYSIZE(big_endian), Perspective::IS_CLIENT));
-  EXPECT_EQ(connection_id, read_connection_id);
-
-  QuicConnectionId read_connection_id2;
-  QuicDataReader reader2(buffer, connection_id.length(), GetParam().endianness);
-  EXPECT_TRUE(reader2.ReadConnectionId(&read_connection_id2,
-                                       QUIC_ARRAYSIZE(big_endian),
-                                       Perspective::IS_SERVER));
-  EXPECT_EQ(connection_id, read_connection_id2);
-}
-
-// TODO(dschinazi) b/120240679 - remove this test once these flags are
-// deprecated: quic_variable_length_connection_ids_(client|server).
-TEST_P(QuicDataWriterTest, WriteConnectionIdClientAllowingVariableLength) {
-  if (!GetQuicRestartFlag(quic_connection_ids_network_byte_order)) {
-    // This test is pointless if the flag is off.
-    return;
-  }
-  SetQuicRestartFlag(quic_variable_length_connection_ids_client, true);
-  SetQuicRestartFlag(quic_variable_length_connection_ids_server, false);
-  QuicConnectionId connection_id =
-      TestConnectionId(UINT64_C(0x0011223344556677));
-  char big_endian[] = {
-      0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
-  };
-  EXPECT_EQ(connection_id.length(), QUIC_ARRAYSIZE(big_endian));
-  ASSERT_LE(connection_id.length(), kQuicMaxConnectionIdLength);
-  char buffer[kQuicMaxConnectionIdLength];
-  QuicDataWriter writer(connection_id.length(), buffer, GetParam().endianness);
-  EXPECT_TRUE(writer.WriteConnectionId(connection_id, Perspective::IS_SERVER));
-  test::CompareCharArraysWithHexError("connection_id", buffer,
-                                      connection_id.length(), big_endian,
-                                      connection_id.length());
-
-  QuicConnectionId read_connection_id;
-  QuicDataReader reader(buffer, connection_id.length(), GetParam().endianness);
-  EXPECT_TRUE(reader.ReadConnectionId(
-      &read_connection_id, QUIC_ARRAYSIZE(big_endian), Perspective::IS_CLIENT));
-  EXPECT_EQ(connection_id, read_connection_id);
-
-  QuicConnectionId read_connection_id2;
-  QuicDataReader reader2(buffer, connection_id.length(), GetParam().endianness);
-  EXPECT_TRUE(reader2.ReadConnectionId(&read_connection_id2,
-                                       QUIC_ARRAYSIZE(big_endian),
-                                       Perspective::IS_SERVER));
-  EXPECT_EQ(connection_id, read_connection_id2);
 }
 
 TEST_P(QuicDataWriterTest, WriteTag) {
@@ -991,6 +907,76 @@
   EXPECT_FALSE(reader.ReadVarInt62(&test_val));
 }
 
+// Test writing varints with a forced length.
+TEST_P(QuicDataWriterTest, VarIntFixedLength) {
+  char buffer[90];
+  memset(buffer, 0, sizeof(buffer));
+  QuicDataWriter writer(sizeof(buffer), static_cast<char*>(buffer),
+                        Endianness::NETWORK_BYTE_ORDER);
+
+  writer.WriteVarInt62(1, VARIABLE_LENGTH_INTEGER_LENGTH_1);
+  writer.WriteVarInt62(1, VARIABLE_LENGTH_INTEGER_LENGTH_2);
+  writer.WriteVarInt62(1, VARIABLE_LENGTH_INTEGER_LENGTH_4);
+  writer.WriteVarInt62(1, VARIABLE_LENGTH_INTEGER_LENGTH_8);
+
+  writer.WriteVarInt62(63, VARIABLE_LENGTH_INTEGER_LENGTH_1);
+  writer.WriteVarInt62(63, VARIABLE_LENGTH_INTEGER_LENGTH_2);
+  writer.WriteVarInt62(63, VARIABLE_LENGTH_INTEGER_LENGTH_4);
+  writer.WriteVarInt62(63, VARIABLE_LENGTH_INTEGER_LENGTH_8);
+
+  writer.WriteVarInt62(64, VARIABLE_LENGTH_INTEGER_LENGTH_2);
+  writer.WriteVarInt62(64, VARIABLE_LENGTH_INTEGER_LENGTH_4);
+  writer.WriteVarInt62(64, VARIABLE_LENGTH_INTEGER_LENGTH_8);
+
+  writer.WriteVarInt62(16383, VARIABLE_LENGTH_INTEGER_LENGTH_2);
+  writer.WriteVarInt62(16383, VARIABLE_LENGTH_INTEGER_LENGTH_4);
+  writer.WriteVarInt62(16383, VARIABLE_LENGTH_INTEGER_LENGTH_8);
+
+  writer.WriteVarInt62(16384, VARIABLE_LENGTH_INTEGER_LENGTH_4);
+  writer.WriteVarInt62(16384, VARIABLE_LENGTH_INTEGER_LENGTH_8);
+
+  writer.WriteVarInt62(1073741823, VARIABLE_LENGTH_INTEGER_LENGTH_4);
+  writer.WriteVarInt62(1073741823, VARIABLE_LENGTH_INTEGER_LENGTH_8);
+
+  writer.WriteVarInt62(1073741824, VARIABLE_LENGTH_INTEGER_LENGTH_8);
+
+  QuicDataReader reader(buffer, sizeof(buffer), Endianness::NETWORK_BYTE_ORDER);
+
+  uint64_t test_val = 0;
+  for (int i = 0; i < 4; ++i) {
+    EXPECT_TRUE(reader.ReadVarInt62(&test_val));
+    EXPECT_EQ(test_val, 1);
+  }
+  for (int i = 0; i < 4; ++i) {
+    EXPECT_TRUE(reader.ReadVarInt62(&test_val));
+    EXPECT_EQ(test_val, 63);
+  }
+
+  for (int i = 0; i < 3; ++i) {
+    EXPECT_TRUE(reader.ReadVarInt62(&test_val));
+    EXPECT_EQ(test_val, 64);
+  }
+  for (int i = 0; i < 3; ++i) {
+    EXPECT_TRUE(reader.ReadVarInt62(&test_val));
+    EXPECT_EQ(test_val, 16383);
+  }
+
+  for (int i = 0; i < 2; ++i) {
+    EXPECT_TRUE(reader.ReadVarInt62(&test_val));
+    EXPECT_EQ(test_val, 16384);
+  }
+  for (int i = 0; i < 2; ++i) {
+    EXPECT_TRUE(reader.ReadVarInt62(&test_val));
+    EXPECT_EQ(test_val, 1073741823);
+  }
+
+  EXPECT_TRUE(reader.ReadVarInt62(&test_val));
+  EXPECT_EQ(test_val, 1073741824);
+
+  // We are at the end of the buffer so this should fail.
+  EXPECT_FALSE(reader.ReadVarInt62(&test_val));
+}
+
 // Test encoding/decoding stream-id values.
 void EncodeDecodeStreamId(uint64_t value_in, bool expected_decode_result) {
   char buffer[1 * kMultiVarCount];
diff --git a/quic/core/quic_default_packet_writer.cc b/quic/core/quic_default_packet_writer.cc
index 2c162e0..3b16a74 100644
--- a/quic/core/quic_default_packet_writer.cc
+++ b/quic/core/quic_default_packet_writer.cc
@@ -30,10 +30,6 @@
   return result;
 }
 
-bool QuicDefaultPacketWriter::IsWriteBlockedDataBuffered() const {
-  return false;
-}
-
 bool QuicDefaultPacketWriter::IsWriteBlocked() const {
   return write_blocked_;
 }
diff --git a/quic/core/quic_default_packet_writer.h b/quic/core/quic_default_packet_writer.h
index 93917da..b5473eb 100644
--- a/quic/core/quic_default_packet_writer.h
+++ b/quic/core/quic_default_packet_writer.h
@@ -30,7 +30,6 @@
                           const QuicIpAddress& self_address,
                           const QuicSocketAddress& peer_address,
                           PerPacketOptions* options) override;
-  bool IsWriteBlockedDataBuffered() const override;
   bool IsWriteBlocked() const override;
   void SetWritable() override;
   QuicByteCount GetMaxPacketSize(
diff --git a/quic/core/quic_dispatcher.cc b/quic/core/quic_dispatcher.cc
index 0cb03aa..f17bdf2 100644
--- a/quic/core/quic_dispatcher.cc
+++ b/quic/core/quic_dispatcher.cc
@@ -96,8 +96,7 @@
                        QuicStreamOffset offset,
                        QuicByteCount data_length,
                        QuicDataWriter* writer) override {
-    QUIC_BUG << "PacketCollector::WriteCryptoData is unimplemented.";
-    return false;
+    return send_buffer_.WriteStreamData(offset, data_length, writer);
   }
 
   std::vector<std::unique_ptr<QuicEncryptedPacket>>* packets() {
@@ -143,7 +142,12 @@
     frame->error_details = error_details;
     // TODO(fayang): Use the right long header type for conneciton close sent by
     // dispatcher.
-    creator_.SetLongHeaderType(RETRY);
+    if (QuicVersionHasLongHeaderLengths(framer_->transport_version())) {
+      creator_.SetLongHeaderType(HANDSHAKE);
+    } else {
+      // TODO(b/123493765): we should probably not be sending RETRY here.
+      creator_.SetLongHeaderType(RETRY);
+    }
     if (!creator_.AddSavedFrame(QuicFrame(frame), NOT_RETRANSMISSION)) {
       QUIC_BUG << "Unable to add frame to an empty packet";
       delete frame;
@@ -165,16 +169,31 @@
     collector_.SaveStatelessRejectFrameData(reject);
     while (offset < reject.length()) {
       QuicFrame frame;
-      creator_.SetLongHeaderType(RETRY);
-      if (!creator_.ConsumeData(
-              QuicUtils::GetCryptoStreamId(framer_->transport_version()),
-              reject.length(), offset, offset,
-              /*fin=*/false,
-              /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame)) {
-        QUIC_BUG << "Unable to consume data into an empty packet.";
-        return;
+      if (QuicVersionHasLongHeaderLengths(framer_->transport_version())) {
+        creator_.SetLongHeaderType(HANDSHAKE);
+      } else {
+        // TODO(b/123493765): we should probably not be sending RETRY here.
+        creator_.SetLongHeaderType(RETRY);
       }
-      offset += frame.stream_frame.data_length;
+      if (framer_->transport_version() < QUIC_VERSION_47) {
+        if (!creator_.ConsumeData(
+                QuicUtils::GetCryptoStreamId(framer_->transport_version()),
+                reject.length(), offset, offset,
+                /*fin=*/false,
+                /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame)) {
+          QUIC_BUG << "Unable to consume data into an empty packet.";
+          return;
+        }
+        offset += frame.stream_frame.data_length;
+      } else {
+        if (!creator_.ConsumeCryptoData(ENCRYPTION_NONE,
+                                        reject.length() - offset, offset,
+                                        NOT_RETRANSMISSION, &frame)) {
+          QUIC_BUG << "Unable to consume crypto data into an empty packet.";
+          return;
+        }
+        offset += frame.crypto_frame->data_length;
+      }
       if (offset < reject.length()) {
         DCHECK(!creator_.HasRoomForStreamFrame(
             QuicUtils::GetCryptoStreamId(framer_->transport_version()), offset,
@@ -267,7 +286,7 @@
 }  // namespace
 
 QuicDispatcher::QuicDispatcher(
-    const QuicConfig& config,
+    const QuicConfig* config,
     const QuicCryptoServerConfig* crypto_config,
     QuicVersionManager* version_manager,
     std::unique_ptr<QuicConnectionHelperInterface> helper,
@@ -404,16 +423,13 @@
       if (ShouldCreateSessionForUnknownVersion(framer_.last_version_label())) {
         return true;
       }
-      if (!GetQuicReloadableFlag(quic_limit_version_negotiation) ||
-          current_packet_->length() >= kMinPacketSizeForVersionNegotiation) {
+      if (current_packet_->length() >= kMinPacketSizeForVersionNegotiation) {
         // Since the version is not supported, send a version negotiation
         // packet and stop processing the current packet.
         time_wait_list_manager()->SendVersionNegotiationPacket(
             connection_id, header.form != GOOGLE_QUIC_PACKET,
             GetSupportedVersions(), current_self_address_,
             current_peer_address_, GetPerPacketContext());
-      } else {
-        QUIC_RELOADABLE_FLAG_COUNT(quic_limit_version_negotiation);
       }
       return false;
     }
@@ -845,6 +861,10 @@
   return false;
 }
 
+void QuicDispatcher::OnCoalescedPacket(const QuicEncryptedPacket& /*packet*/) {
+  DCHECK(false);
+}
+
 bool QuicDispatcher::OnStreamFrame(const QuicStreamFrame& /*frame*/) {
   DCHECK(false);
   return false;
@@ -1202,7 +1222,7 @@
     ChloAlpnExtractor alpn_extractor;
     if (FLAGS_quic_allow_chlo_buffering &&
         !ChloExtractor::Extract(*current_packet_, GetSupportedVersions(),
-                                config_.create_session_tag_indicators(),
+                                config_->create_session_tag_indicators(),
                                 &alpn_extractor)) {
       // Buffer non-CHLO packets.
       ProcessUnauthenticatedHeaderFate(kFateBuffer, connection_id, form,
@@ -1224,7 +1244,7 @@
                           current_peer_address_, current_self_address_,
                           rejector.get());
   if (!ChloExtractor::Extract(*current_packet_, GetSupportedVersions(),
-                              config_.create_session_tag_indicators(),
+                              config_->create_session_tag_indicators(),
                               &validator)) {
     ProcessUnauthenticatedHeaderFate(kFateBuffer, connection_id, form, version);
     return;
@@ -1281,12 +1301,6 @@
   current_packet_ = current_packet.get();
   current_connection_id_ = rejector->connection_id();
   framer_.set_version(first_version);
-  if (GetQuicReloadableFlag(quic_fix_last_packet_is_ietf_quic)) {
-    if (GetLastPacketFormat() != current_packet_format) {
-      QUIC_RELOADABLE_FLAG_COUNT(quic_fix_last_packet_is_ietf_quic);
-    }
-    framer_.set_last_packet_form(current_packet_format);
-  }
 
   // Stop buffering packets on this connection
   const auto num_erased =
@@ -1385,8 +1399,4 @@
   framer_.set_validate_flags(false);
 }
 
-PacketHeaderFormat QuicDispatcher::GetLastPacketFormat() const {
-  return framer_.GetLastPacketFormat();
-}
-
 }  // namespace quic
diff --git a/quic/core/quic_dispatcher.h b/quic/core/quic_dispatcher.h
index d6eb35b..b0c2509 100644
--- a/quic/core/quic_dispatcher.h
+++ b/quic/core/quic_dispatcher.h
@@ -44,7 +44,7 @@
   // Ideally we'd have a linked_hash_set: the  boolean is unused.
   typedef QuicLinkedHashMap<QuicBlockedWriterInterface*, bool> WriteBlockedList;
 
-  QuicDispatcher(const QuicConfig& config,
+  QuicDispatcher(const QuicConfig* config,
                  const QuicCryptoServerConfig* crypto_config,
                  QuicVersionManager* version_manager,
                  std::unique_ptr<QuicConnectionHelperInterface> helper,
@@ -143,6 +143,7 @@
       const QuicVersionNegotiationPacket& packet) override;
   void OnDecryptedPacket(EncryptionLevel level) override;
   bool OnPacketHeader(const QuicPacketHeader& header) override;
+  void OnCoalescedPacket(const QuicEncryptedPacket& packet) override;
   bool OnStreamFrame(const QuicStreamFrame& frame) override;
   bool OnCryptoFrame(const QuicCryptoFrame& frame) override;
   bool OnAckFrameStart(QuicPacketNumber largest_acked,
@@ -271,7 +272,7 @@
   }
   const QuicReceivedPacket& current_packet() const { return *current_packet_; }
 
-  const QuicConfig& config() const { return config_; }
+  const QuicConfig& config() const { return *config_; }
 
   const QuicCryptoServerConfig* crypto_config() const { return crypto_config_; }
 
@@ -351,11 +352,6 @@
   // Skip validating that the public flags are set to legal values.
   void DisableFlagValidation();
 
-  // Please do not use this method.
-  // TODO(fayang): Remove this method when deprecating
-  // quic_fix_last_packet_is_ietf_quic flag.
-  PacketHeaderFormat GetLastPacketFormat() const;
-
  private:
   friend class test::QuicDispatcherPeer;
   friend class StatelessRejectorProcessDoneCallback;
@@ -410,7 +406,7 @@
     new_sessions_allowed_per_event_loop_ = new_sessions_allowed_per_event_loop;
   }
 
-  const QuicConfig& config_;
+  const QuicConfig* config_;
 
   const QuicCryptoServerConfig* crypto_config_;
 
diff --git a/quic/core/quic_dispatcher_test.cc b/quic/core/quic_dispatcher_test.cc
index 0381cf5..7fc1263 100644
--- a/quic/core/quic_dispatcher_test.cc
+++ b/quic/core/quic_dispatcher_test.cc
@@ -72,7 +72,7 @@
   TestQuicSpdyServerSession& operator=(const TestQuicSpdyServerSession&) =
       delete;
 
-  ~TestQuicSpdyServerSession() override { delete connection(); };
+  ~TestQuicSpdyServerSession() override { delete connection(); }
 
   MOCK_METHOD3(OnConnectionClosed,
                void(QuicErrorCode error,
@@ -114,7 +114,7 @@
 
 class TestDispatcher : public QuicDispatcher {
  public:
-  TestDispatcher(const QuicConfig& config,
+  TestDispatcher(const QuicConfig* config,
                  const QuicCryptoServerConfig* crypto_config,
                  QuicVersionManager* version_manager)
       : QuicDispatcher(config,
@@ -157,7 +157,6 @@
   using QuicDispatcher::current_client_address;
   using QuicDispatcher::current_peer_address;
   using QuicDispatcher::current_self_address;
-  using QuicDispatcher::GetLastPacketFormat;
   using QuicDispatcher::writer;
 };
 
@@ -207,7 +206,8 @@
                        std::move(proof_source),
                        KeyExchangeSource::Default(),
                        TlsServerHandshaker::CreateSslCtx()),
-        dispatcher_(new NiceMock<TestDispatcher>(config_,
+        server_address_(QuicIpAddress::Any4(), 5),
+        dispatcher_(new NiceMock<TestDispatcher>(&config_,
                                                  &crypto_config_,
                                                  &version_manager_)),
         time_wait_list_manager_(nullptr),
@@ -370,9 +370,12 @@
 };
 
 TEST_F(QuicDispatcherTest, TlsClientHelloCreatesSession) {
+  if (CurrentSupportedVersions().front().transport_version < QUIC_VERSION_47) {
+    // TLS is only supported in versions 47 and greater.
+    return;
+  }
   FLAGS_quic_supports_tls_handshake = true;
   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
-  server_address_ = QuicSocketAddress(QuicIpAddress::Any4(), 5);
 
   EXPECT_CALL(*dispatcher_,
               CreateQuicSession(TestConnectionId(1), client_address,
@@ -400,7 +403,6 @@
 
 TEST_F(QuicDispatcherTest, ProcessPackets) {
   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
-  server_address_ = QuicSocketAddress(QuicIpAddress::Any4(), 5);
 
   EXPECT_CALL(*dispatcher_,
               CreateQuicSession(TestConnectionId(1), client_address,
@@ -448,7 +450,6 @@
 // Regression test of b/93325907.
 TEST_F(QuicDispatcherTest, DispatcherDoesNotRejectPacketNumberZero) {
   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
-  server_address_ = QuicSocketAddress(QuicIpAddress::Any4(), 5);
 
   EXPECT_CALL(*dispatcher_,
               CreateQuicSession(TestConnectionId(1), client_address,
@@ -486,9 +487,7 @@
 
 TEST_F(QuicDispatcherTest, StatelessVersionNegotiation) {
   CreateTimeWaitListManager();
-  SetQuicReloadableFlag(quic_limit_version_negotiation, false);
   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
-  server_address_ = QuicSocketAddress(QuicIpAddress::Any4(), 5);
 
   EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0);
   EXPECT_CALL(*time_wait_list_manager_,
@@ -497,16 +496,17 @@
   QuicTransportVersion version =
       static_cast<QuicTransportVersion>(QuicTransportVersionMin() - 1);
   ParsedQuicVersion parsed_version(PROTOCOL_QUIC_CRYPTO, version);
-  ProcessPacket(client_address, TestConnectionId(1), true, parsed_version,
-                SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
-                PACKET_4BYTE_PACKET_NUMBER, 1);
+  // Pad the CHLO message with enough data to make the packet large enough
+  // to trigger version negotiation.
+  QuicString chlo = SerializeCHLO() + QuicString(1200, 'a');
+  DCHECK_LE(1200u, chlo.length());
+  ProcessPacket(client_address, TestConnectionId(1), true, parsed_version, chlo,
+                PACKET_8BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER, 1);
 }
 
 TEST_F(QuicDispatcherTest, NoVersionNegotiationWithSmallPacket) {
   CreateTimeWaitListManager();
-  SetQuicReloadableFlag(quic_limit_version_negotiation, true);
   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
-  server_address_ = QuicSocketAddress(QuicIpAddress::Any4(), 5);
 
   EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0);
   EXPECT_CALL(*time_wait_list_manager_,
@@ -515,10 +515,12 @@
   QuicTransportVersion version =
       static_cast<QuicTransportVersion>(QuicTransportVersionMin() - 1);
   ParsedQuicVersion parsed_version(PROTOCOL_QUIC_CRYPTO, version);
-  QuicString chlo = SerializeCHLO();
+  QuicString chlo = SerializeCHLO() + QuicString(1200, 'a');
   // Truncate to 1100 bytes of payload which results in a packet just
   // under 1200 bytes after framing, packet, and encryption overhead.
+  DCHECK_LE(1200u, chlo.length());
   QuicString truncated_chlo = chlo.substr(0, 1100);
+  DCHECK_EQ(1100u, truncated_chlo.length());
   ProcessPacket(client_address, TestConnectionId(1), true, parsed_version,
                 truncated_chlo, PACKET_8BYTE_CONNECTION_ID,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
@@ -607,7 +609,6 @@
   CreateTimeWaitListManager();
 
   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 0);
-  server_address_ = QuicSocketAddress(QuicIpAddress::Any4(), 5);
 
   // dispatcher_ should drop this packet.
   EXPECT_CALL(*dispatcher_,
@@ -623,7 +624,6 @@
 TEST_F(QuicDispatcherTest, OKSeqNoPacketProcessed) {
   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
   QuicConnectionId connection_id = TestConnectionId(1);
-  server_address_ = QuicSocketAddress(QuicIpAddress::Any4(), 5);
 
   EXPECT_CALL(*dispatcher_,
               CreateQuicSession(TestConnectionId(1), client_address,
@@ -681,17 +681,15 @@
 }
 
 TEST_F(QuicDispatcherTest, SupportedTransportVersionsChangeInFlight) {
-  static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 8u,
+  static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 6u,
                 "Supported versions out of sync");
-  SetQuicReloadableFlag(quic_disable_version_35, false);
+  SetQuicReloadableFlag(quic_disable_version_39, false);
   SetQuicReloadableFlag(quic_enable_version_43, true);
   SetQuicReloadableFlag(quic_enable_version_44, true);
-  SetQuicReloadableFlag(quic_enable_version_45, true);
   SetQuicReloadableFlag(quic_enable_version_46, true);
   SetQuicReloadableFlag(quic_enable_version_47, true);
   SetQuicReloadableFlag(quic_enable_version_99, true);
   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
-  server_address_ = QuicSocketAddress(QuicIpAddress::Any4(), 5);
   uint64_t conn_id = 1;
   QuicConnectionId connection_id = TestConnectionId(conn_id);
 
@@ -808,39 +806,6 @@
                 SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
 
-  // Turn off version 45.
-  SetQuicReloadableFlag(quic_enable_version_45, false);
-  connection_id = TestConnectionId(++conn_id);
-  EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
-                                              QuicStringPiece("hq"), _))
-      .Times(0);
-  ProcessPacket(client_address, connection_id, true,
-                ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_45),
-                SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
-                PACKET_4BYTE_PACKET_NUMBER, 1);
-
-  // Turn on version 45.
-  SetQuicReloadableFlag(quic_enable_version_45, true);
-  connection_id = TestConnectionId(++conn_id);
-  EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
-                                              QuicStringPiece("hq"), _))
-      .WillOnce(testing::Return(CreateSession(
-          dispatcher_.get(), config_, connection_id, client_address,
-          &mock_helper_, &mock_alarm_factory_, &crypto_config_,
-          QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
-  EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
-              ProcessUdpPacket(_, _, _))
-      .WillOnce(WithArg<2>(
-          Invoke([this, connection_id](const QuicEncryptedPacket& packet) {
-            ValidatePacket(connection_id, packet);
-          })));
-  EXPECT_CALL(*dispatcher_,
-              ShouldCreateOrBufferPacketForConnection(connection_id, _));
-  ProcessPacket(client_address, connection_id, true,
-                ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_45),
-                SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
-                PACKET_4BYTE_PACKET_NUMBER, 1);
-
   // Turn off version 44.
   SetQuicReloadableFlag(quic_enable_version_44, false);
   connection_id = TestConnectionId(++conn_id);
@@ -907,19 +872,19 @@
                 SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
 
-  // Turn off version 35.
-  SetQuicReloadableFlag(quic_disable_version_35, true);
+  // Turn off version 39.
+  SetQuicReloadableFlag(quic_disable_version_39, true);
   connection_id = TestConnectionId(++conn_id);
   EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
                                               QuicStringPiece("hq"), _))
       .Times(0);
   ProcessPacket(client_address, connection_id, true,
-                ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_35),
+                ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_39),
                 SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
 
-  // Turn on version 35.
-  SetQuicReloadableFlag(quic_disable_version_35, false);
+  // Turn on version 39.
+  SetQuicReloadableFlag(quic_disable_version_39, false);
   connection_id = TestConnectionId(++conn_id);
   EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
                                               QuicStringPiece("hq"), _))
@@ -936,7 +901,7 @@
   EXPECT_CALL(*dispatcher_,
               ShouldCreateOrBufferPacketForConnection(connection_id, _));
   ProcessPacket(client_address, connection_id, true,
-                ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_35),
+                ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_39),
                 SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
 }
@@ -1067,9 +1032,9 @@
 // Parameterized test for stateless rejects.  Should test all
 // combinations of enabling/disabling, reject/no-reject for stateless
 // rejects.
-INSTANTIATE_TEST_CASE_P(QuicDispatcherStatelessRejectTests,
-                        QuicDispatcherStatelessRejectTest,
-                        ::testing::ValuesIn(GetStatelessRejectTestParams()));
+INSTANTIATE_TEST_SUITE_P(QuicDispatcherStatelessRejectTests,
+                         QuicDispatcherStatelessRejectTest,
+                         ::testing::ValuesIn(GetStatelessRejectTestParams()));
 
 TEST_P(QuicDispatcherStatelessRejectTest, ParameterizedBasicTest) {
   CreateTimeWaitListManager();
@@ -1624,6 +1589,7 @@
  public:
   BufferedPacketStoreTest()
       : QuicDispatcherTest(),
+        server_addr_(QuicSocketAddress(QuicIpAddress::Any4(), 5)),
         client_addr_(QuicIpAddress::Loopback4(), 1234),
         signed_config_(new QuicSignedServerConfig) {
     SetQuicReloadableFlag(quic_use_cheap_stateless_rejects,
@@ -1660,7 +1626,7 @@
   CryptoHandshakeMessage full_chlo_;
 };
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     BufferedPacketStoreTests,
     BufferedPacketStoreTest,
     ::testing::ValuesIn(GetBufferedPacketStoreTestParams()));
@@ -1668,7 +1634,6 @@
 TEST_P(BufferedPacketStoreTest, ProcessNonChloPacketsUptoLimitAndProcessChlo) {
   InSequence s;
   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
-  server_address_ = QuicSocketAddress(QuicIpAddress::Any4(), 5);
   QuicConnectionId conn_id = TestConnectionId(1);
   // A bunch of non-CHLO should be buffered upon arrival, and the first one
   // should trigger ShouldCreateOrBufferPacketForConnection().
@@ -1708,7 +1673,6 @@
 TEST_P(BufferedPacketStoreTest,
        ProcessNonChloPacketsForDifferentConnectionsUptoLimit) {
   InSequence s;
-  server_address_ = QuicSocketAddress(QuicIpAddress::Any4(), 5);
   // A bunch of non-CHLO should be buffered upon arrival.
   size_t kNumConnections = kMaxConnectionsWithoutCHLO + 1;
   for (size_t i = 1; i <= kNumConnections; ++i) {
@@ -1781,7 +1745,6 @@
 TEST_P(BufferedPacketStoreTest, ReceiveRetransmittedCHLO) {
   InSequence s;
   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
-  server_address_ = QuicSocketAddress(QuicIpAddress::Any4(), 5);
   QuicConnectionId conn_id = TestConnectionId(1);
   ProcessPacket(client_address, conn_id, true, QuicStrCat("data packet ", 2),
                 PACKET_8BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER,
@@ -1817,7 +1780,6 @@
   QuicBufferedPacketStorePeer::set_clock(store, mock_helper_.GetClock());
 
   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
-  server_address_ = QuicSocketAddress(QuicIpAddress::Any4(), 5);
   QuicConnectionId conn_id = TestConnectionId(1);
   ProcessPacket(client_address, conn_id, true, QuicStrCat("data packet ", 2),
                 PACKET_8BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER,
@@ -2125,6 +2087,7 @@
         client_addr_(QuicIpAddress::Loopback4(), 1234),
         client_addr_2_(QuicIpAddress::Loopback4(), 1357),
         crypto_config_peer_(&crypto_config_),
+        server_addr_(QuicIpAddress::Any4(), 5),
         signed_config_(new QuicSignedServerConfig) {
     SetQuicReloadableFlag(enable_quic_stateless_reject_support, true);
     SetQuicReloadableFlag(quic_use_cheap_stateless_rejects, true);
@@ -2722,16 +2685,17 @@
   // different endianness which causes the client to close the connection
   // because of QUIC_INVALID_STREAM_DATA.
 
-  SetQuicReloadableFlag(quic_disable_version_35, false);
-  ParsedQuicVersion chlo_version(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_39);
+  SetQuicReloadableFlag(quic_disable_version_39, false);
+  SetQuicReloadableFlag(quic_enable_version_43, true);
+  ParsedQuicVersion chlo_version(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43);
   chlo_.SetVersion(kVER, chlo_version);
-  // Send a CHLO with v39. Dispatcher framer's version is set to v39.
+  // Send a CHLO with v43. Dispatcher framer's version is set to v43.
   ProcessPacket(client_addr_, TestConnectionId(1), true, chlo_version,
                 SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
 
-  // Send another CHLO with v35. Dispatcher framer's version is set to v35.
-  chlo_version.transport_version = QUIC_VERSION_35;
+  // Send another CHLO with v39. Dispatcher framer's version is set to v39.
+  chlo_version.transport_version = QUIC_VERSION_39;
   chlo_.SetVersion(kVER, chlo_version);
   // Invalidate the cached serialized form.
   chlo_.MarkDirty();
@@ -2740,7 +2704,7 @@
                 PACKET_4BYTE_PACKET_NUMBER, 1);
   ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 2);
 
-  // Complete the ProofSource::GetProof call for v39. This would cause the
+  // Complete the ProofSource::GetProof call for v43. This would cause the
   // version mismatch between the CHLO packet and the dispatcher.
   GetFakeProofSource()->InvokePendingCallback(0);
   ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1);
@@ -2748,15 +2712,12 @@
 
 // Regression test for b/116200989.
 TEST_F(AsyncGetProofTest, DispatcherHasWrongLastPacketIsIetfQuic) {
-  SetQuicReloadableFlag(quic_fix_last_packet_is_ietf_quic, true);
-
   // Process a packet of v44.
   ParsedQuicVersion chlo_version(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44);
   chlo_.SetVersion(kVER, chlo_version);
   ProcessPacket(client_addr_, TestConnectionId(1), true, chlo_version,
                 SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
-  EXPECT_NE(GOOGLE_QUIC_PACKET, dispatcher_->GetLastPacketFormat());
 
   // Process another packet of v43.
   chlo_version.transport_version = QUIC_VERSION_43;
@@ -2766,18 +2727,14 @@
   ProcessPacket(client_addr_, TestConnectionId(2), true, chlo_version,
                 SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
-  EXPECT_EQ(GOOGLE_QUIC_PACKET, dispatcher_->GetLastPacketFormat());
   ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 2);
 
   // Complete the ProofSource::GetProof call for v44.
   GetFakeProofSource()->InvokePendingCallback(0);
-  // Verify the last_packet_is_ietf_quic gets reset properly.
-  EXPECT_NE(GOOGLE_QUIC_PACKET, dispatcher_->GetLastPacketFormat());
   ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1);
 
   // Complete the ProofSource::GetProof call for v43.
   GetFakeProofSource()->InvokePendingCallback(0);
-  EXPECT_EQ(GOOGLE_QUIC_PACKET, dispatcher_->GetLastPacketFormat());
   ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0);
 }
 
diff --git a/quic/core/quic_epoll_alarm_factory.cc b/quic/core/quic_epoll_alarm_factory.cc
index 2ec2f6f..2d6efdf 100644
--- a/quic/core/quic_epoll_alarm_factory.cc
+++ b/quic/core/quic_epoll_alarm_factory.cc
@@ -69,7 +69,7 @@
 QuicEpollAlarmFactory::QuicEpollAlarmFactory(QuicEpollServer* epoll_server)
     : epoll_server_(epoll_server) {}
 
-QuicEpollAlarmFactory::~QuicEpollAlarmFactory() {}
+QuicEpollAlarmFactory::~QuicEpollAlarmFactory() = default;
 
 QuicAlarm* QuicEpollAlarmFactory::CreateAlarm(QuicAlarm::Delegate* delegate) {
   return new QuicEpollAlarm(epoll_server_,
diff --git a/quic/core/quic_epoll_alarm_factory_test.cc b/quic/core/quic_epoll_alarm_factory_test.cc
index b8d4caf..08a7cfe 100644
--- a/quic/core/quic_epoll_alarm_factory_test.cc
+++ b/quic/core/quic_epoll_alarm_factory_test.cc
@@ -40,9 +40,8 @@
   QuicConnectionArena arena_;
 };
 
-INSTANTIATE_TEST_CASE_P(UseArena,
-                        QuicEpollAlarmFactoryTest,
-                        ::testing::ValuesIn({true, false}));
+INSTANTIATE_TEST_SUITE_P(UseArena, QuicEpollAlarmFactoryTest,
+                         ::testing::ValuesIn({true, false}));
 
 TEST_P(QuicEpollAlarmFactoryTest, CreateAlarm) {
   QuicArenaScopedPtr<TestDelegate> delegate =
diff --git a/quic/core/quic_epoll_connection_helper.cc b/quic/core/quic_epoll_connection_helper.cc
index 49002f6..e05139f 100644
--- a/quic/core/quic_epoll_connection_helper.cc
+++ b/quic/core/quic_epoll_connection_helper.cc
@@ -22,7 +22,7 @@
       random_generator_(QuicRandom::GetInstance()),
       allocator_type_(type) {}
 
-QuicEpollConnectionHelper::~QuicEpollConnectionHelper() {}
+QuicEpollConnectionHelper::~QuicEpollConnectionHelper() = default;
 
 const QuicClock* QuicEpollConnectionHelper::GetClock() const {
   return &clock_;
diff --git a/quic/core/quic_epoll_connection_helper.h b/quic/core/quic_epoll_connection_helper.h
index 62e030c..8b2a469 100644
--- a/quic/core/quic_epoll_connection_helper.h
+++ b/quic/core/quic_epoll_connection_helper.h
@@ -56,8 +56,6 @@
   QuicBufferAllocator* GetStreamSendBufferAllocator() override;
 
  private:
-  friend class QuicEpollConnectionHelperPeer;
-
   const QuicEpollClock clock_;
   QuicRandom* random_generator_;
   // Set up allocators.  They take up minimal memory before use.
diff --git a/quic/core/quic_error_codes.cc b/quic/core/quic_error_codes.cc
index a6259e9..215f527 100644
--- a/quic/core/quic_error_codes.cc
+++ b/quic/core/quic_error_codes.cc
@@ -155,6 +155,7 @@
     RETURN_STRING_LITERAL(QUIC_STREAM_ID_BLOCKED_ERROR);
     RETURN_STRING_LITERAL(QUIC_MAX_STREAM_ID_ERROR);
     RETURN_STRING_LITERAL(QUIC_HTTP_DECODER_ERROR);
+    RETURN_STRING_LITERAL(QUIC_STALE_CONNECTION_CANCELLED);
 
     RETURN_STRING_LITERAL(QUIC_LAST_ERROR);
     // Intentionally have no default case, so we'll break the build
diff --git a/quic/core/quic_error_codes.h b/quic/core/quic_error_codes.h
index ebf15f5..d3446a8 100644
--- a/quic/core/quic_error_codes.h
+++ b/quic/core/quic_error_codes.h
@@ -325,9 +325,11 @@
   QUIC_MAX_STREAM_ID_ERROR = 119,
   // Error in Http decoder
   QUIC_HTTP_DECODER_ERROR = 120,
+  // Connection from stale host needs to be cancelled.
+  QUIC_STALE_CONNECTION_CANCELLED = 121,
 
   // No error. Used as bound while iterating.
-  QUIC_LAST_ERROR = 121,
+  QUIC_LAST_ERROR = 122,
 };
 // QuicErrorCodes is encoded as a single octet on-the-wire.
 static_assert(static_cast<int>(QUIC_LAST_ERROR) <=
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc
index 1b93074..854b8c7 100644
--- a/quic/core/quic_framer.cc
+++ b/quic/core/quic_framer.cc
@@ -4,6 +4,7 @@
 
 #include "net/third_party/quiche/src/quic/core/quic_framer.h"
 
+#include <cstddef>
 #include <cstdint>
 #include <memory>
 
@@ -23,12 +24,14 @@
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_aligned.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_stack_trace.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
@@ -186,7 +189,7 @@
 uint8_t PacketNumberLengthToOnWireValue(
     QuicTransportVersion version,
     QuicPacketNumberLength packet_number_length) {
-  if (version > QUIC_VERSION_46) {
+  if (version > QUIC_VERSION_44) {
     return packet_number_length - 1;
   }
   switch (packet_number_length) {
@@ -209,7 +212,7 @@
     QuicPacketNumberLength* packet_number_length) {
   DCHECK(!(type & FLAGS_LONG_HEADER));
   const bool two_bits_packet_number_length =
-      infer_packet_header_type_from_version ? version > QUIC_VERSION_46
+      infer_packet_header_type_from_version ? version > QUIC_VERSION_44
                                             : (type & FLAGS_FIXED_BIT);
   if (two_bits_packet_number_length) {
     *packet_number_length =
@@ -237,13 +240,13 @@
                                     QuicLongHeaderType type) {
   switch (type) {
     case INITIAL:
-      return version > QUIC_VERSION_46 ? 0 : 0x7F;
+      return version > QUIC_VERSION_44 ? 0 : 0x7F;
     case ZERO_RTT_PROTECTED:
-      return version > QUIC_VERSION_46 ? 1 << 4 : 0x7C;
+      return version > QUIC_VERSION_44 ? 1 << 4 : 0x7C;
     case HANDSHAKE:
-      return version > QUIC_VERSION_46 ? 2 << 4 : 0x7D;
+      return version > QUIC_VERSION_44 ? 2 << 4 : 0x7D;
     case RETRY:
-      return version > QUIC_VERSION_46 ? 3 << 4 : 0x7E;
+      return version > QUIC_VERSION_44 ? 3 << 4 : 0x7E;
     case VERSION_NEGOTIATION:
       return 0xF0;  // Value does not matter
     default:
@@ -256,7 +259,7 @@
                        uint8_t type,
                        QuicLongHeaderType* long_header_type) {
   DCHECK((type & FLAGS_LONG_HEADER) && version != QUIC_VERSION_UNSUPPORTED);
-  if (version > QUIC_VERSION_46) {
+  if (version > QUIC_VERSION_44) {
     switch ((type & 0x30) >> 4) {
       case 0:
         *long_header_type = INITIAL;
@@ -303,7 +306,7 @@
 QuicPacketNumberLength GetLongHeaderPacketNumberLength(
     QuicTransportVersion version,
     uint8_t type) {
-  if (version > QUIC_VERSION_46) {
+  if (version > QUIC_VERSION_44) {
     return static_cast<QuicPacketNumberLength>((type & 0x03) + 1);
   }
   return PACKET_4BYTE_PACKET_NUMBER;
@@ -343,6 +346,63 @@
           version == QUIC_VERSION_99);
 }
 
+// Convert a stream ID to a count of streams, for IETF QUIC/Version 99 only.
+// There is no need to take into account whether the ID is for uni- or
+// bi-directional streams, or whether it's server- or client- initiated.  It
+// always returns a valid count.
+QuicStreamId StreamIdToCount(QuicTransportVersion version,
+                             QuicStreamId stream_id) {
+  DCHECK_EQ(QUIC_VERSION_99, version);
+  if ((stream_id & 0x3) == 0) {
+    return (stream_id / QuicUtils::StreamIdDelta(version));
+  }
+  return (stream_id / QuicUtils::StreamIdDelta(version)) + 1;
+}
+
+// Returns the maximum value that a stream count may have, taking into account
+// the fact that bidirectional, client initiated, streams have one fewer stream
+// available than the others. This is because the old crypto streams, with ID ==
+// 0 are not included in the count.
+// The version is not included in the call, nor does the method take the version
+// into account, because this is called only from code used for IETF QUIC.
+// TODO(fkastenholz): Remove this method and replace calls to it with direct
+// references to kMaxQuicStreamIdCount when streamid 0 becomes a normal stream
+// id.
+QuicStreamId GetMaxStreamCount(bool unidirectional, Perspective perspective) {
+  if (!unidirectional && perspective == Perspective::IS_CLIENT) {
+    return kMaxQuicStreamId >> 2;
+  }
+  return (kMaxQuicStreamId >> 2) + 1;
+}
+
+// Convert a stream count to the maximum stream ID for that count.
+// Needs to know whether the resulting stream ID  should be uni-directional,
+// bi-directional, server-initiated, or client-initiated.
+// Returns true if it works, false if not. The only error condition is that
+// the stream_count is too big and it would generate a stream id that is larger
+// than the implementation's maximum stream id value.
+bool StreamCountToId(QuicStreamId stream_count,
+                     bool unidirectional,
+                     Perspective perspective,
+                     QuicTransportVersion version,
+                     QuicStreamId* generated_stream_id) {
+  DCHECK_EQ(QUIC_VERSION_99, version);
+  // TODO(fkastenholz): when the MAX_STREAMS and STREAMS_BLOCKED frames
+  // are connected all the way up to the stream_id_manager, handle count==0
+  // properly (interpret it as "can open 0 streams") and the count being too
+  // large (close the connection).
+  if ((stream_count == 0) ||
+      (stream_count > GetMaxStreamCount(unidirectional, perspective))) {
+    return false;
+  }
+  *generated_stream_id =
+      ((unidirectional)
+           ? QuicUtils::GetFirstUnidirectionalStreamId(version, perspective)
+           : QuicUtils::GetFirstBidirectionalStreamId(version, perspective)) +
+      ((stream_count - 1) * QuicUtils::StreamIdDelta(version));
+  return true;
+}
+
 }  // namespace
 
 QuicFramer::QuicFramer(const ParsedQuicVersionVector& supported_versions,
@@ -352,7 +412,6 @@
       error_(QUIC_NO_ERROR),
       last_serialized_connection_id_(EmptyQuicConnectionId()),
       last_version_label_(0),
-      last_header_form_(GOOGLE_QUIC_PACKET),
       version_(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED),
       supported_versions_(supported_versions),
       decrypter_level_(ENCRYPTION_NONE),
@@ -485,7 +544,7 @@
   if (version != QUIC_VERSION_99) {
     return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize;
   }
-  if (frame.stream_id == 0) {
+  if (frame.stream_id == QuicUtils::GetInvalidStreamId(version)) {
     // Frame would be a MAX DATA frame, which has only a Maximum Data field.
     return kQuicFrameTypeSize +
            QuicDataWriter::GetVarInt62Len(frame.byte_offset);
@@ -498,24 +557,32 @@
 }
 
 // static
-size_t QuicFramer::GetMaxStreamIdFrameSize(QuicTransportVersion version,
-                                           const QuicMaxStreamIdFrame& frame) {
+size_t QuicFramer::GetMaxStreamsFrameSize(QuicTransportVersion version,
+                                          const QuicMaxStreamIdFrame& frame) {
   if (version != QUIC_VERSION_99) {
     QUIC_BUG << "In version " << version
              << " - not 99 - and tried to serialize MaxStreamId Frame.";
   }
-  return kQuicFrameTypeSize +
-         QuicDataWriter::GetVarInt62Len(frame.max_stream_id);
+
+  // Convert from the stream id on which the connection is blocked to a count
+  QuicStreamId stream_count = StreamIdToCount(version, frame.max_stream_id);
+
+  return kQuicFrameTypeSize + QuicDataWriter::GetVarInt62Len(stream_count);
 }
+
 // static
-size_t QuicFramer::GetStreamIdBlockedFrameSize(
+size_t QuicFramer::GetStreamsBlockedFrameSize(
     QuicTransportVersion version,
     const QuicStreamIdBlockedFrame& frame) {
   if (version != QUIC_VERSION_99) {
     QUIC_BUG << "In version " << version
              << " - not 99 - and tried to serialize StreamIdBlocked Frame.";
   }
-  return kQuicFrameTypeSize + QuicDataWriter::GetVarInt62Len(frame.stream_id);
+
+  // Convert from the stream id on which the connection is blocked to a count
+  QuicStreamId stream_count = StreamIdToCount(version, frame.stream_id);
+
+  return kQuicFrameTypeSize + QuicDataWriter::GetVarInt62Len(stream_count);
 }
 
 // static
@@ -524,10 +591,7 @@
   if (version != QUIC_VERSION_99) {
     return kQuicFrameTypeSize + kQuicMaxStreamIdSize;
   }
-  // TODO(fkastenholz): This should be converted to use
-  // QuicUtils::GetInvalidStreamId to get the correct invalid stream id value
-  // and not rely on 0.
-  if (frame.stream_id == 0) {
+  if (frame.stream_id == QuicUtils::GetInvalidStreamId(version)) {
     // return size of IETF QUIC Blocked frame
     return kQuicFrameTypeSize + QuicDataWriter::GetVarInt62Len(frame.offset);
   }
@@ -591,10 +655,9 @@
     case NEW_TOKEN_FRAME:
       return GetNewTokenFrameSize(*frame.new_token_frame);
     case MAX_STREAM_ID_FRAME:
-      return GetMaxStreamIdFrameSize(version, frame.max_stream_id_frame);
+      return GetMaxStreamsFrameSize(version, frame.max_stream_id_frame);
     case STREAM_ID_BLOCKED_FRAME:
-      return GetStreamIdBlockedFrameSize(version,
-                                         frame.stream_id_blocked_frame);
+      return GetStreamsBlockedFrameSize(version, frame.stream_id_blocked_frame);
     case PATH_RESPONSE_FRAME:
       return GetPathResponseFrameSize(*frame.path_response_frame);
     case PATH_CHALLENGE_FRAME:
@@ -761,19 +824,63 @@
 
 QuicFramer::AckFrameInfo::~AckFrameInfo() {}
 
+bool QuicFramer::WriteIetfLongHeaderLength(const QuicPacketHeader& header,
+                                           QuicDataWriter* writer,
+                                           size_t length_field_offset,
+                                           EncryptionLevel level) {
+  if (!QuicVersionHasLongHeaderLengths(transport_version()) ||
+      !header.version_flag || length_field_offset == 0) {
+    return true;
+  }
+  if (writer->length() < length_field_offset ||
+      writer->length() - length_field_offset <
+          kQuicDefaultLongHeaderLengthLength) {
+    set_detailed_error("Invalid length_field_offset.");
+    QUIC_BUG << "Invalid length_field_offset.";
+    return false;
+  }
+  size_t length_to_write = writer->length() - length_field_offset -
+                           kQuicDefaultLongHeaderLengthLength;
+  // Add length of auth tag.
+  length_to_write = GetCiphertextSize(level, length_to_write);
+
+  QuicDataWriter length_writer(writer->length() - length_field_offset,
+                               writer->data() + length_field_offset);
+  if (!length_writer.WriteVarInt62(length_to_write,
+                                   kQuicDefaultLongHeaderLengthLength)) {
+    set_detailed_error("Failed to overwrite long header length.");
+    QUIC_BUG << "Failed to overwrite long header length.";
+    return false;
+  }
+  return true;
+}
+
 size_t QuicFramer::BuildDataPacket(const QuicPacketHeader& header,
                                    const QuicFrames& frames,
                                    char* buffer,
-                                   size_t packet_length) {
-  if (version_.transport_version == QUIC_VERSION_99) {
-    return BuildIetfDataPacket(header, frames, buffer, packet_length);
-  }
-  QuicDataWriter writer(packet_length, buffer, endianness());
-  if (!AppendPacketHeader(header, &writer)) {
+                                   size_t packet_length,
+                                   EncryptionLevel level) {
+  QuicDataWriter writer(packet_length, buffer);
+  size_t length_field_offset = 0;
+  if (!AppendPacketHeader(header, &writer, &length_field_offset)) {
     QUIC_BUG << "AppendPacketHeader failed";
     return 0;
   }
 
+  if (transport_version() == QUIC_VERSION_99) {
+    if (AppendIetfFrames(frames, &writer) == 0) {
+      return 0;
+    }
+    if (!WriteIetfLongHeaderLength(header, &writer, length_field_offset,
+                                   level)) {
+      return 0;
+    }
+    return writer.length();
+  }
+  // TODO(dschinazi) if we enable long header lengths before v99, we need to
+  // add support for fixing up lengths in QuicFramer::BuildDataPacket.
+  DCHECK(!QuicVersionHasLongHeaderLengths(transport_version()));
+
   size_t i = 0;
   for (const QuicFrame& frame : frames) {
     // Determine if we should write stream frame length in header.
@@ -893,173 +1000,11 @@
         }
         break;
       case CRYPTO_FRAME:
-        set_detailed_error(
-            "Attempt to append CRYPTO frame and not in version 99.");
-        return RaiseError(QUIC_INTERNAL_ERROR);
-      default:
-        RaiseError(QUIC_INVALID_FRAME_DATA);
-        QUIC_BUG << "QUIC_INVALID_FRAME_DATA";
-        return 0;
-    }
-    ++i;
-  }
-
-  return writer.length();
-}
-
-size_t QuicFramer::BuildIetfDataPacket(const QuicPacketHeader& header,
-                                       const QuicFrames& frames,
-                                       char* buffer,
-                                       size_t packet_length) {
-  QuicDataWriter writer(packet_length, buffer, endianness());
-  if (!AppendIetfPacketHeader(header, &writer)) {
-    QUIC_BUG << "AppendPacketHeader failed";
-    return 0;
-  }
-
-  size_t i = 0;
-  for (const QuicFrame& frame : frames) {
-    // Determine if we should write stream frame length in header.
-    const bool last_frame_in_packet = i == frames.size() - 1;
-    if (!AppendIetfTypeByte(frame, last_frame_in_packet, &writer)) {
-      QUIC_BUG << "AppendIetfTypeByte failed";
-      return 0;
-    }
-
-    switch (frame.type) {
-      case PADDING_FRAME:
-        if (!AppendPaddingFrame(frame.padding_frame, &writer)) {
-          QUIC_BUG << "AppendPaddingFrame of "
-                   << frame.padding_frame.num_padding_bytes << " failed";
-          return 0;
+        if (version_.transport_version < QUIC_VERSION_47) {
+          set_detailed_error(
+              "Attempt to append CRYPTO frame in version prior to 47.");
+          return RaiseError(QUIC_INTERNAL_ERROR);
         }
-        break;
-      case STREAM_FRAME:
-        if (!AppendStreamFrame(frame.stream_frame, last_frame_in_packet,
-                               &writer)) {
-          QUIC_BUG << "AppendStreamFrame failed";
-          return 0;
-        }
-        break;
-      case ACK_FRAME:
-        if (!AppendIetfAckFrameAndTypeByte(*frame.ack_frame, &writer)) {
-          QUIC_BUG << "AppendAckFrameAndTypeByte failed";
-          return 0;
-        }
-        break;
-      case STOP_WAITING_FRAME:
-        set_detailed_error(
-            "Attempt to append STOP WAITING frame in version 99.");
-        return RaiseError(QUIC_INTERNAL_ERROR);
-      case MTU_DISCOVERY_FRAME:
-        // MTU discovery frames are serialized as ping frames.
-        QUIC_FALLTHROUGH_INTENDED;
-      case PING_FRAME:
-        // Ping has no payload.
-        break;
-      case RST_STREAM_FRAME:
-        if (!AppendRstStreamFrame(*frame.rst_stream_frame, &writer)) {
-          QUIC_BUG << "AppendRstStreamFrame failed";
-          return 0;
-        }
-        break;
-      case CONNECTION_CLOSE_FRAME:
-        if (!AppendConnectionCloseFrame(*frame.connection_close_frame,
-                                        &writer)) {
-          QUIC_BUG << "AppendConnectionCloseFrame failed";
-          return 0;
-        }
-        break;
-      case GOAWAY_FRAME:
-        set_detailed_error("Attempt to append GOAWAY frame in version 99.");
-        return RaiseError(QUIC_INTERNAL_ERROR);
-      case WINDOW_UPDATE_FRAME:
-        // Depending on whether there is a stream ID or not, will be either a
-        // MAX STREAM DATA frame or a MAX DATA frame.
-        if (frame.window_update_frame->stream_id == 0) {
-          if (!AppendMaxDataFrame(*frame.window_update_frame, &writer)) {
-            QUIC_BUG << "AppendMaxDataFrame failed";
-            return 0;
-          }
-        } else {
-          if (!AppendMaxStreamDataFrame(*frame.window_update_frame, &writer)) {
-            QUIC_BUG << "AppendMaxStreamDataFrame failed";
-            return 0;
-          }
-        }
-        break;
-      case BLOCKED_FRAME:
-        if (!AppendBlockedFrame(*frame.blocked_frame, &writer)) {
-          QUIC_BUG << "AppendBlockedFrame failed";
-          return 0;
-        }
-        break;
-      case APPLICATION_CLOSE_FRAME:
-        if (!AppendApplicationCloseFrame(*frame.application_close_frame,
-                                         &writer)) {
-          QUIC_BUG << "AppendApplicationCloseFrame failed";
-          return 0;
-        }
-        break;
-      case MAX_STREAM_ID_FRAME:
-        if (!AppendMaxStreamIdFrame(frame.max_stream_id_frame, &writer)) {
-          QUIC_BUG << "AppendMaxStreamIdFrame failed";
-          return 0;
-        }
-        break;
-      case STREAM_ID_BLOCKED_FRAME:
-        if (!AppendStreamIdBlockedFrame(frame.stream_id_blocked_frame,
-                                        &writer)) {
-          QUIC_BUG << "AppendMaxStreamIdFrame failed";
-          return 0;
-        }
-        break;
-      case NEW_CONNECTION_ID_FRAME:
-        if (!AppendNewConnectionIdFrame(*frame.new_connection_id_frame,
-                                        &writer)) {
-          QUIC_BUG << "AppendNewConnectionIdFrame failed";
-          return 0;
-        }
-        break;
-      case RETIRE_CONNECTION_ID_FRAME:
-        if (!AppendRetireConnectionIdFrame(*frame.retire_connection_id_frame,
-                                           &writer)) {
-          QUIC_BUG << "AppendRetireConnectionIdFrame failed";
-          return 0;
-        }
-        break;
-      case NEW_TOKEN_FRAME:
-        if (!AppendNewTokenFrame(*frame.new_token_frame, &writer)) {
-          QUIC_BUG << "AppendNewTokenFrame failed";
-          return 0;
-        }
-        break;
-      case STOP_SENDING_FRAME:
-        if (!AppendStopSendingFrame(*frame.stop_sending_frame, &writer)) {
-          QUIC_BUG << "AppendStopSendingFrame failed";
-          return 0;
-        }
-        break;
-      case PATH_CHALLENGE_FRAME:
-        if (!AppendPathChallengeFrame(*frame.path_challenge_frame, &writer)) {
-          QUIC_BUG << "AppendPathChallengeFrame failed";
-          return 0;
-        }
-        break;
-      case PATH_RESPONSE_FRAME:
-        if (!AppendPathResponseFrame(*frame.path_response_frame, &writer)) {
-          QUIC_BUG << "AppendPathResponseFrame failed";
-          return 0;
-        }
-        break;
-      case MESSAGE_FRAME:
-        if (!AppendMessageFrameAndTypeByte(*frame.message_frame,
-                                           last_frame_in_packet, &writer)) {
-          QUIC_BUG << "AppendMessageFrame failed";
-          return 0;
-        }
-        break;
-      case CRYPTO_FRAME:
         if (!AppendCryptoFrame(*frame.crypto_frame, &writer)) {
           QUIC_BUG << "AppendCryptoFrame failed";
           return 0;
@@ -1076,10 +1021,176 @@
   return writer.length();
 }
 
-size_t QuicFramer::BuildIetfConnectivityProbingPacket(
+size_t QuicFramer::AppendIetfFrames(const QuicFrames& frames,
+                                    QuicDataWriter* writer) {
+  size_t i = 0;
+  for (const QuicFrame& frame : frames) {
+    // Determine if we should write stream frame length in header.
+    const bool last_frame_in_packet = i == frames.size() - 1;
+    if (!AppendIetfTypeByte(frame, last_frame_in_packet, writer)) {
+      QUIC_BUG << "AppendIetfTypeByte failed: " << detailed_error();
+      return 0;
+    }
+
+    switch (frame.type) {
+      case PADDING_FRAME:
+        if (!AppendPaddingFrame(frame.padding_frame, writer)) {
+          QUIC_BUG << "AppendPaddingFrame of "
+                   << frame.padding_frame.num_padding_bytes
+                   << " failed: " << detailed_error();
+          return 0;
+        }
+        break;
+      case STREAM_FRAME:
+        if (!AppendStreamFrame(frame.stream_frame, last_frame_in_packet,
+                               writer)) {
+          QUIC_BUG << "AppendStreamFrame failed: " << detailed_error();
+          return 0;
+        }
+        break;
+      case ACK_FRAME:
+        if (!AppendIetfAckFrameAndTypeByte(*frame.ack_frame, writer)) {
+          QUIC_BUG << "AppendAckFrameAndTypeByte failed: " << detailed_error();
+          return 0;
+        }
+        break;
+      case STOP_WAITING_FRAME:
+        set_detailed_error(
+            "Attempt to append STOP WAITING frame in version 99.");
+        return RaiseError(QUIC_INTERNAL_ERROR);
+      case MTU_DISCOVERY_FRAME:
+        // MTU discovery frames are serialized as ping frames.
+        QUIC_FALLTHROUGH_INTENDED;
+      case PING_FRAME:
+        // Ping has no payload.
+        break;
+      case RST_STREAM_FRAME:
+        if (!AppendRstStreamFrame(*frame.rst_stream_frame, writer)) {
+          QUIC_BUG << "AppendRstStreamFrame failed: " << detailed_error();
+          return 0;
+        }
+        break;
+      case CONNECTION_CLOSE_FRAME:
+        if (!AppendConnectionCloseFrame(*frame.connection_close_frame,
+                                        writer)) {
+          QUIC_BUG << "AppendConnectionCloseFrame failed: " << detailed_error();
+          return 0;
+        }
+        break;
+      case GOAWAY_FRAME:
+        set_detailed_error("Attempt to append GOAWAY frame in version 99.");
+        return RaiseError(QUIC_INTERNAL_ERROR);
+      case WINDOW_UPDATE_FRAME:
+        // Depending on whether there is a stream ID or not, will be either a
+        // MAX STREAM DATA frame or a MAX DATA frame.
+        if (frame.window_update_frame->stream_id ==
+            QuicUtils::GetInvalidStreamId(transport_version())) {
+          if (!AppendMaxDataFrame(*frame.window_update_frame, writer)) {
+            QUIC_BUG << "AppendMaxDataFrame failed: " << detailed_error();
+            return 0;
+          }
+        } else {
+          if (!AppendMaxStreamDataFrame(*frame.window_update_frame, writer)) {
+            QUIC_BUG << "AppendMaxStreamDataFrame failed: " << detailed_error();
+            return 0;
+          }
+        }
+        break;
+      case BLOCKED_FRAME:
+        if (!AppendBlockedFrame(*frame.blocked_frame, writer)) {
+          QUIC_BUG << "AppendBlockedFrame failed: " << detailed_error();
+          return 0;
+        }
+        break;
+      case APPLICATION_CLOSE_FRAME:
+        if (!AppendApplicationCloseFrame(*frame.application_close_frame,
+                                         writer)) {
+          QUIC_BUG << "AppendApplicationCloseFrame failed: "
+                   << detailed_error();
+          return 0;
+        }
+        break;
+      case MAX_STREAM_ID_FRAME:
+        if (!AppendMaxStreamsFrame(frame.max_stream_id_frame, writer)) {
+          QUIC_BUG << "AppendMaxStreamsFrame failed" << detailed_error();
+          return 0;
+        }
+        break;
+      case STREAM_ID_BLOCKED_FRAME:
+        if (!AppendStreamsBlockedFrame(frame.stream_id_blocked_frame, writer)) {
+          QUIC_BUG << "AppendStreamsBlockedFrame failed" << detailed_error();
+          return 0;
+        }
+        break;
+      case NEW_CONNECTION_ID_FRAME:
+        if (!AppendNewConnectionIdFrame(*frame.new_connection_id_frame,
+                                        writer)) {
+          QUIC_BUG << "AppendNewConnectionIdFrame failed: " << detailed_error();
+          return 0;
+        }
+        break;
+      case RETIRE_CONNECTION_ID_FRAME:
+        if (!AppendRetireConnectionIdFrame(*frame.retire_connection_id_frame,
+                                           writer)) {
+          QUIC_BUG << "AppendRetireConnectionIdFrame failed: "
+                   << detailed_error();
+          return 0;
+        }
+        break;
+      case NEW_TOKEN_FRAME:
+        if (!AppendNewTokenFrame(*frame.new_token_frame, writer)) {
+          QUIC_BUG << "AppendNewTokenFrame failed: " << detailed_error();
+          return 0;
+        }
+        break;
+      case STOP_SENDING_FRAME:
+        if (!AppendStopSendingFrame(*frame.stop_sending_frame, writer)) {
+          QUIC_BUG << "AppendStopSendingFrame failed: " << detailed_error();
+          return 0;
+        }
+        break;
+      case PATH_CHALLENGE_FRAME:
+        if (!AppendPathChallengeFrame(*frame.path_challenge_frame, writer)) {
+          QUIC_BUG << "AppendPathChallengeFrame failed: " << detailed_error();
+          return 0;
+        }
+        break;
+      case PATH_RESPONSE_FRAME:
+        if (!AppendPathResponseFrame(*frame.path_response_frame, writer)) {
+          QUIC_BUG << "AppendPathResponseFrame failed: " << detailed_error();
+          return 0;
+        }
+        break;
+      case MESSAGE_FRAME:
+        if (!AppendMessageFrameAndTypeByte(*frame.message_frame,
+                                           last_frame_in_packet, writer)) {
+          QUIC_BUG << "AppendMessageFrame failed: " << detailed_error();
+          return 0;
+        }
+        break;
+      case CRYPTO_FRAME:
+        if (!AppendCryptoFrame(*frame.crypto_frame, writer)) {
+          QUIC_BUG << "AppendCryptoFrame failed: " << detailed_error();
+          return 0;
+        }
+        break;
+      default:
+        RaiseError(QUIC_INVALID_FRAME_DATA);
+        set_detailed_error("Tried to append unknown frame type.");
+        QUIC_BUG << "QUIC_INVALID_FRAME_DATA";
+        return 0;
+    }
+    ++i;
+  }
+
+  return writer->length();
+}
+
+size_t QuicFramer::BuildConnectivityProbingPacketNew(
     const QuicPacketHeader& header,
     char* buffer,
-    size_t packet_length) {
+    size_t packet_length,
+    EncryptionLevel level) {
   QuicFrames frames;
 
   // Write a PING frame, which has no data payload.
@@ -1090,19 +1201,26 @@
   QuicPaddingFrame padding_frame;
   frames.push_back(QuicFrame(padding_frame));
 
-  return BuildIetfDataPacket(header, frames, buffer, packet_length);
+  return BuildDataPacket(header, frames, buffer, packet_length, level);
 }
 
 size_t QuicFramer::BuildConnectivityProbingPacket(
     const QuicPacketHeader& header,
     char* buffer,
-    size_t packet_length) {
-  if (version_.transport_version == QUIC_VERSION_99) {
-    return BuildIetfConnectivityProbingPacket(header, buffer, packet_length);
+    size_t packet_length,
+    EncryptionLevel level) {
+  if (transport_version() == QUIC_VERSION_99 ||
+      QuicVersionHasLongHeaderLengths(transport_version()) ||
+      GetQuicReloadableFlag(quic_simplify_build_connectivity_probing_packet)) {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_simplify_build_connectivity_probing_packet);
+    // TODO(rch): Remove this method when the flag is deprecated.
+    return BuildConnectivityProbingPacketNew(header, buffer, packet_length,
+                                             level);
   }
-  QuicDataWriter writer(packet_length, buffer, endianness());
 
-  if (!AppendPacketHeader(header, &writer)) {
+  QuicDataWriter writer(packet_length, buffer);
+
+  if (!AppendPacketHeader(header, &writer, nullptr)) {
     QUIC_BUG << "AppendPacketHeader failed";
     return 0;
   }
@@ -1133,7 +1251,8 @@
     char* buffer,
     size_t packet_length,
     QuicPathFrameBuffer* payload,
-    QuicRandom* randomizer) {
+    QuicRandom* randomizer,
+    EncryptionLevel level) {
   if (version_.transport_version != QUIC_VERSION_99) {
     QUIC_BUG << "Attempt to build a PATH_CHALLENGE Connectivity Probing "
                 "packet and not doing IETF QUIC";
@@ -1152,7 +1271,7 @@
   QuicPaddingFrame padding_frame;
   frames.push_back(QuicFrame(padding_frame));
 
-  return BuildIetfDataPacket(header, frames, buffer, packet_length);
+  return BuildDataPacket(header, frames, buffer, packet_length, level);
 }
 
 size_t QuicFramer::BuildPathResponsePacket(
@@ -1160,7 +1279,8 @@
     char* buffer,
     size_t packet_length,
     const QuicDeque<QuicPathFrameBuffer>& payloads,
-    const bool is_padded) {
+    const bool is_padded,
+    EncryptionLevel level) {
   if (payloads.empty()) {
     QUIC_BUG
         << "Attempt to generate connectivity response with no request payloads";
@@ -1192,7 +1312,7 @@
     frames.push_back(QuicFrame(padding_frame));
   }
 
-  return BuildIetfDataPacket(header, frames, buffer, packet_length);
+  return BuildDataPacket(header, frames, buffer, packet_length, level);
 }
 
 // static
@@ -1221,7 +1341,7 @@
   std::unique_ptr<char[]> buffer(new char[len]);
   // Endianness is not a concern here, as writer is not going to write integers
   // or floating numbers.
-  QuicDataWriter writer(len, buffer.get(), NETWORK_BYTE_ORDER);
+  QuicDataWriter writer(len, buffer.get());
 
   uint8_t flags = static_cast<uint8_t>(PACKET_PUBLIC_FLAGS_RST |
                                        PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID);
@@ -1231,7 +1351,7 @@
     return nullptr;
   }
 
-  if (!writer.WriteConnectionId(packet.connection_id, Perspective::IS_SERVER)) {
+  if (!writer.WriteConnectionId(packet.connection_id)) {
     return nullptr;
   }
 
@@ -1255,7 +1375,7 @@
   size_t len = kPacketHeaderTypeSize + random_bytes_length +
                sizeof(stateless_reset_token);
   std::unique_ptr<char[]> buffer(new char[len]);
-  QuicDataWriter writer(len, buffer.get(), NETWORK_BYTE_ORDER);
+  QuicDataWriter writer(len, buffer.get());
 
   uint8_t type = 0;
   type |= FLAGS_FIXED_BIT;
@@ -1306,7 +1426,7 @@
   std::unique_ptr<char[]> buffer(new char[len]);
   // Endianness is not a concern here, version negotiation packet does not have
   // integers or floating numbers.
-  QuicDataWriter writer(len, buffer.get(), NETWORK_BYTE_ORDER);
+  QuicDataWriter writer(len, buffer.get());
 
   uint8_t flags = static_cast<uint8_t>(
       PACKET_PUBLIC_FLAGS_VERSION | PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID |
@@ -1316,7 +1436,7 @@
     return nullptr;
   }
 
-  if (!writer.WriteConnectionId(connection_id, Perspective::IS_SERVER)) {
+  if (!writer.WriteConnectionId(connection_id)) {
     return nullptr;
   }
 
@@ -1342,7 +1462,7 @@
                PACKET_8BYTE_CONNECTION_ID +
                (versions.size() + 1) * kQuicVersionSize;
   std::unique_ptr<char[]> buffer(new char[len]);
-  QuicDataWriter writer(len, buffer.get(), NETWORK_BYTE_ORDER);
+  QuicDataWriter writer(len, buffer.get());
 
   // TODO(fayang): Randomly select a value for the type.
   uint8_t type = static_cast<uint8_t>(FLAGS_LONG_HEADER | VERSION_NEGOTIATION);
@@ -1356,8 +1476,7 @@
 
   if (!AppendIetfConnectionId(true, EmptyQuicConnectionId(),
                               PACKET_0BYTE_CONNECTION_ID, connection_id,
-                              PACKET_8BYTE_CONNECTION_ID, &writer,
-                              Perspective::IS_SERVER)) {
+                              PACKET_8BYTE_CONNECTION_ID, &writer)) {
     return nullptr;
   }
 
@@ -1373,7 +1492,7 @@
 }
 
 bool QuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) {
-  QuicDataReader reader(packet.data(), packet.length(), endianness());
+  QuicDataReader reader(packet.data(), packet.length());
 
   bool last_packet_is_ietf_quic = false;
   if (infer_packet_header_type_from_version_) {
@@ -1384,7 +1503,6 @@
   }
   if (last_packet_is_ietf_quic) {
     QUIC_DVLOG(1) << ENDPOINT << "Processing IETF QUIC packet.";
-    reader.set_endianness(NETWORK_BYTE_ORDER);
   }
 
   visitor_->OnPacket();
@@ -1397,7 +1515,6 @@
     DCHECK_NE("", detailed_error_);
     return RaiseError(QUIC_INVALID_PACKET_HEADER);
   }
-  last_header_form_ = header.form;
 
   if (!visitor_->OnUnauthenticatedPublicHeader(header)) {
     // The visitor suppresses further processing of the packet.
@@ -1411,9 +1528,6 @@
     }
   }
 
-  // framer's version may change, reset reader's endianness.
-  reader.set_endianness(endianness());
-
   bool rv;
   if (IsVersionNegotiation(header, last_packet_is_ietf_quic)) {
     QUIC_DVLOG(1) << ENDPOINT << "Received version negotiation packet";
@@ -1467,6 +1581,119 @@
   return true;
 }
 
+bool QuicFramer::MaybeProcessIetfInitialRetryToken(
+    QuicDataReader* encrypted_reader,
+    QuicPacketHeader* header) {
+  if (!QuicVersionHasLongHeaderLengths(header->version.transport_version) ||
+      header->form != IETF_QUIC_LONG_HEADER_PACKET ||
+      header->long_packet_type != INITIAL) {
+    return true;
+  }
+  uint64_t retry_token_length = 0;
+  header->retry_token_length_length = encrypted_reader->PeekVarInt62Length();
+  if (!encrypted_reader->ReadVarInt62(&retry_token_length)) {
+    set_detailed_error("Unable to read INITIAL retry token length.");
+    return RaiseError(QUIC_INVALID_PACKET_HEADER);
+  }
+  header->retry_token = encrypted_reader->PeekRemainingPayload();
+  // Safety check to avoid spending ressources if malformed.
+  // At this point header->retry_token contains the rest of the packet
+  // so its length() is the amount of data remaining in the packet.
+  if (retry_token_length > header->retry_token.length()) {
+    set_detailed_error("INITIAL token length longer than packet.");
+    return RaiseError(QUIC_INVALID_PACKET_HEADER);
+  }
+  // Resize retry_token to make it only contain the retry token.
+  header->retry_token.remove_suffix(header->retry_token.length() -
+                                    retry_token_length);
+  // Advance encrypted_reader by retry_token_length.
+  uint8_t wasted_byte;
+  for (uint64_t i = 0; i < retry_token_length; ++i) {
+    if (!encrypted_reader->ReadUInt8(&wasted_byte)) {
+      set_detailed_error("Unable to read INITIAL retry token.");
+      return RaiseError(QUIC_INVALID_PACKET_HEADER);
+    }
+  }
+  return true;
+}
+
+// Seeks the current packet to check for a coalesced packet at the end.
+// If the IETF length field only spans part of the outer packet,
+// then there is a coalesced packet after this one.
+void QuicFramer::MaybeProcessCoalescedPacket(
+    const QuicDataReader& encrypted_reader,
+    uint64_t remaining_bytes_length,
+    const QuicPacketHeader& header) {
+  if (header.remaining_packet_length >= remaining_bytes_length) {
+    // There is no coalesced packet.
+    return;
+  }
+
+  QuicStringPiece remaining_data = encrypted_reader.PeekRemainingPayload();
+  DCHECK_EQ(remaining_data.length(), remaining_bytes_length);
+
+  const char* coalesced_data =
+      remaining_data.data() + header.remaining_packet_length;
+  uint64_t coalesced_data_length =
+      remaining_bytes_length - header.remaining_packet_length;
+  QuicDataReader coalesced_reader(coalesced_data, coalesced_data_length);
+
+  QuicPacketHeader coalesced_header;
+  if (!ProcessIetfPacketHeader(&coalesced_reader, &coalesced_header)) {
+    QUIC_PEER_BUG << ENDPOINT
+                  << "Failed to parse received coalesced header of length "
+                  << coalesced_data_length << ": "
+                  << QuicTextUtils::HexEncode(coalesced_data,
+                                              coalesced_data_length)
+                  << " previous header was " << header;
+    return;
+  }
+
+  if (coalesced_header.destination_connection_id !=
+          header.destination_connection_id ||
+      (coalesced_header.form != IETF_QUIC_SHORT_HEADER_PACKET &&
+       coalesced_header.version != header.version)) {
+    QUIC_PEER_BUG << ENDPOINT << "Received mismatched coalesced header "
+                  << coalesced_header << " previous header was " << header;
+    return;
+  }
+
+  QuicEncryptedPacket coalesced_packet(coalesced_data, coalesced_data_length,
+                                       /*owns_buffer=*/false);
+  visitor_->OnCoalescedPacket(coalesced_packet);
+}
+
+bool QuicFramer::MaybeProcessIetfLength(QuicDataReader* encrypted_reader,
+                                        QuicPacketHeader* header) {
+  if (!QuicVersionHasLongHeaderLengths(header->version.transport_version) ||
+      header->form != IETF_QUIC_LONG_HEADER_PACKET ||
+      (header->long_packet_type != INITIAL &&
+       header->long_packet_type != HANDSHAKE &&
+       header->long_packet_type != ZERO_RTT_PROTECTED)) {
+    return true;
+  }
+  header->length_length = encrypted_reader->PeekVarInt62Length();
+  if (!encrypted_reader->ReadVarInt62(&header->remaining_packet_length)) {
+    set_detailed_error("Unable to read long header payload length.");
+    return RaiseError(QUIC_INVALID_PACKET_HEADER);
+  }
+  uint64_t remaining_bytes_length = encrypted_reader->BytesRemaining();
+  if (header->remaining_packet_length > remaining_bytes_length) {
+    set_detailed_error("Long header payload length longer than packet.");
+    return RaiseError(QUIC_INVALID_PACKET_HEADER);
+  }
+
+  MaybeProcessCoalescedPacket(*encrypted_reader, remaining_bytes_length,
+                              *header);
+
+  if (!encrypted_reader->TruncateRemaining(header->remaining_packet_length)) {
+    set_detailed_error("Length TruncateRemaining failed.");
+    QUIC_BUG << "Length TruncateRemaining failed.";
+    return RaiseError(QUIC_INVALID_PACKET_HEADER);
+  }
+  return true;
+}
+
 bool QuicFramer::ProcessIetfDataPacket(QuicDataReader* encrypted_reader,
                                        QuicPacketHeader* header,
                                        const QuicEncryptedPacket& packet,
@@ -1474,6 +1701,10 @@
                                        size_t buffer_length) {
   DCHECK_NE(GOOGLE_QUIC_PACKET, header->form);
   DCHECK(!header->has_possible_stateless_reset_token);
+  header->retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_0;
+  header->retry_token = QuicStringPiece();
+  header->length_length = VARIABLE_LENGTH_INTEGER_LENGTH_0;
+  header->remaining_packet_length = 0;
   if (header->form == IETF_QUIC_SHORT_HEADER_PACKET &&
       perspective_ == Perspective::IS_CLIENT) {
     // Peek possible stateless reset token. Will only be used on decryption
@@ -1488,6 +1719,14 @@
     }
   }
 
+  if (!MaybeProcessIetfInitialRetryToken(encrypted_reader, header)) {
+    return false;
+  }
+
+  if (!MaybeProcessIetfLength(encrypted_reader, header)) {
+    return false;
+  }
+
   if (header->form == IETF_QUIC_SHORT_HEADER_PACKET ||
       header->long_packet_type != VERSION_NEGOTIATION) {
     // Process packet number.
@@ -1518,7 +1757,8 @@
   // using QUIC crypto.
   if (header->form == IETF_QUIC_LONG_HEADER_PACKET &&
       header->long_packet_type == ZERO_RTT_PROTECTED &&
-      perspective_ == Perspective::IS_CLIENT) {
+      perspective_ == Perspective::IS_CLIENT &&
+      version_.handshake_protocol == PROTOCOL_QUIC_CRYPTO) {
     if (!encrypted_reader->ReadBytes(
             reinterpret_cast<uint8_t*>(last_nonce_.data()),
             last_nonce_.size())) {
@@ -1537,8 +1777,17 @@
     return false;
   }
 
+  QuicStringPiece encrypted = encrypted_reader->ReadRemainingPayload();
+  QuicStringPiece associated_data = GetAssociatedDataFromEncryptedPacket(
+      version_.transport_version, packet,
+      header->destination_connection_id_length,
+      header->source_connection_id_length, header->version_flag,
+      header->nonce != nullptr, header->packet_number_length,
+      header->retry_token_length_length, header->retry_token.length(),
+      header->length_length);
+
   size_t decrypted_length = 0;
-  if (!DecryptPayload(encrypted_reader, *header, packet, decrypted_buffer,
+  if (!DecryptPayload(encrypted, associated_data, *header, decrypted_buffer,
                       buffer_length, &decrypted_length)) {
     if (IsIetfStatelessResetPacket(*header)) {
       // This is a stateless reset packet.
@@ -1550,7 +1799,7 @@
     set_detailed_error("Unable to decrypt payload.");
     return RaiseError(QUIC_DECRYPTION_FAILURE);
   }
-  QuicDataReader reader(decrypted_buffer, decrypted_length, endianness());
+  QuicDataReader reader(decrypted_buffer, decrypted_length);
 
   // Update the largest packet number after we have decrypted the packet
   // so we are confident is not attacker controlled.
@@ -1609,14 +1858,23 @@
     return false;
   }
 
+  QuicStringPiece encrypted = encrypted_reader->ReadRemainingPayload();
+  QuicStringPiece associated_data = GetAssociatedDataFromEncryptedPacket(
+      version_.transport_version, packet,
+      header->destination_connection_id_length,
+      header->source_connection_id_length, header->version_flag,
+      header->nonce != nullptr, header->packet_number_length,
+      header->retry_token_length_length, header->retry_token.length(),
+      header->length_length);
+
   size_t decrypted_length = 0;
-  if (!DecryptPayload(encrypted_reader, *header, packet, decrypted_buffer,
+  if (!DecryptPayload(encrypted, associated_data, *header, decrypted_buffer,
                       buffer_length, &decrypted_length)) {
     set_detailed_error("Unable to decrypt payload.");
     return RaiseError(QUIC_DECRYPTION_FAILURE);
   }
 
-  QuicDataReader reader(decrypted_buffer, decrypted_length, endianness());
+  QuicDataReader reader(decrypted_buffer, decrypted_length);
 
   // Update the largest packet number after we have decrypted the packet
   // so we are confident is not attacker controlled.
@@ -1704,9 +1962,10 @@
 }
 
 bool QuicFramer::AppendPacketHeader(const QuicPacketHeader& header,
-                                    QuicDataWriter* writer) {
+                                    QuicDataWriter* writer,
+                                    size_t* length_field_offset) {
   if (transport_version() > QUIC_VERSION_43) {
-    return AppendIetfPacketHeader(header, writer);
+    return AppendIetfPacketHeader(header, writer, length_field_offset);
   }
   QUIC_DVLOG(1) << ENDPOINT << "Appending header: " << header;
   uint8_t public_flags = 0;
@@ -1745,8 +2004,7 @@
         public_flags |= PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID_OLD;
       }
       if (!writer->WriteUInt8(public_flags) ||
-          !writer->WriteConnectionId(header.destination_connection_id,
-                                     perspective_)) {
+          !writer->WriteConnectionId(header.destination_connection_id)) {
         return false;
       }
       break;
@@ -1781,7 +2039,7 @@
 bool QuicFramer::AppendIetfHeaderTypeByte(const QuicPacketHeader& header,
                                           QuicDataWriter* writer) {
   uint8_t type = 0;
-  if (transport_version() > QUIC_VERSION_46) {
+  if (transport_version() > QUIC_VERSION_44) {
     if (header.version_flag) {
       type = static_cast<uint8_t>(
           FLAGS_LONG_HEADER | FLAGS_FIXED_BIT |
@@ -1814,7 +2072,8 @@
 }
 
 bool QuicFramer::AppendIetfPacketHeader(const QuicPacketHeader& header,
-                                        QuicDataWriter* writer) {
+                                        QuicDataWriter* writer,
+                                        size_t* length_field_offset) {
   QUIC_DVLOG(1) << ENDPOINT << "Appending IETF header: " << header;
   QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion(
       header.destination_connection_id, transport_version()))
@@ -1838,11 +2097,34 @@
   if (!AppendIetfConnectionId(
           header.version_flag, header.destination_connection_id,
           header.destination_connection_id_length, header.source_connection_id,
-          header.source_connection_id_length, writer, perspective_)) {
+          header.source_connection_id_length, writer)) {
     return false;
   }
   last_serialized_connection_id_ = header.destination_connection_id;
 
+  if (QuicVersionHasLongHeaderLengths(transport_version()) &&
+      header.version_flag) {
+    if (header.long_packet_type == INITIAL) {
+      // Write retry token length.
+      if (!writer->WriteVarInt62(header.retry_token.length(),
+                                 header.retry_token_length_length)) {
+        return false;
+      }
+      // Write retry token.
+      if (!header.retry_token.empty() &&
+          !writer->WriteStringPiece(header.retry_token)) {
+        return false;
+      }
+    }
+    if (length_field_offset != nullptr) {
+      *length_field_offset = writer->length();
+    }
+    // Add fake length to reserve two bytes to add length in later.
+    writer->WriteVarInt62(256);
+  } else if (length_field_offset != nullptr) {
+    *length_field_offset = 0;
+  }
+
   // Append packet number.
   if (!AppendPacketNumber(header.packet_number_length, header.packet_number,
                           writer)) {
@@ -1943,8 +2225,7 @@
   switch (public_flags & PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID) {
     case PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID:
       if (!reader->ReadConnectionId(&header->destination_connection_id,
-                                    kQuicDefaultConnectionIdLength,
-                                    perspective_)) {
+                                    kQuicDefaultConnectionIdLength)) {
         set_detailed_error("Unable to read ConnectionId.");
         return false;
       }
@@ -2133,7 +2414,7 @@
     } else {
       header->version = ParseQuicVersionLabel(version_label);
       if (header->version.transport_version != QUIC_VERSION_UNSUPPORTED) {
-        if (header->version.transport_version > QUIC_VERSION_46 &&
+        if (header->version.transport_version > QUIC_VERSION_44 &&
             !(type & FLAGS_FIXED_BIT)) {
           set_detailed_error("Fixed bit is 0 in long header.");
           return false;
@@ -2170,7 +2451,7 @@
     header->destination_connection_id = last_serialized_connection_id_;
   }
   if (infer_packet_header_type_from_version_ &&
-      transport_version() > QUIC_VERSION_46 && !(type & FLAGS_FIXED_BIT)) {
+      transport_version() > QUIC_VERSION_44 && !(type & FLAGS_FIXED_BIT)) {
     set_detailed_error("Fixed bit is 0 in short header.");
     return false;
   }
@@ -2218,14 +2499,14 @@
   // Read connection ID.
   if (header->destination_connection_id_length == PACKET_8BYTE_CONNECTION_ID &&
       !reader->ReadConnectionId(&header->destination_connection_id,
-                                kQuicDefaultConnectionIdLength, perspective_)) {
+                                kQuicDefaultConnectionIdLength)) {
     set_detailed_error("Unable to read Destination ConnectionId.");
     return false;
   }
 
   if (header->source_connection_id_length == PACKET_8BYTE_CONNECTION_ID &&
       !reader->ReadConnectionId(&header->source_connection_id,
-                                kQuicDefaultConnectionIdLength, perspective_)) {
+                                kQuicDefaultConnectionIdLength)) {
     set_detailed_error("Unable to read Source ConnectionId.");
     return false;
   }
@@ -2428,6 +2709,22 @@
         }
         break;
       }
+      case CRYPTO_FRAME: {
+        if (version_.transport_version < QUIC_VERSION_47) {
+          set_detailed_error("Illegal frame type.");
+          return RaiseError(QUIC_INVALID_FRAME_DATA);
+        }
+        QuicCryptoFrame frame;
+        if (!ProcessCryptoFrame(reader, &frame)) {
+          return RaiseError(QUIC_INVALID_FRAME_DATA);
+        }
+        if (!visitor_->OnCryptoFrame(frame)) {
+          QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+          // Returning true since there was no parsing error.
+          return true;
+        }
+        break;
+      }
 
       default:
         set_detailed_error("Illegal frame type.");
@@ -2556,9 +2853,10 @@
           }
           break;
         }
-        case IETF_MAX_STREAM_ID: {
+        case IETF_MAX_STREAMS_BIDIRECTIONAL:
+        case IETF_MAX_STREAMS_UNIDIRECTIONAL: {
           QuicMaxStreamIdFrame frame;
-          if (!ProcessMaxStreamIdFrame(reader, &frame)) {
+          if (!ProcessMaxStreamsFrame(reader, &frame, frame_type)) {
             return RaiseError(QUIC_MAX_STREAM_ID_DATA);
           }
           QUIC_CODE_COUNT_N(max_stream_id_received, 1, 2);
@@ -2603,9 +2901,10 @@
           }
           break;
         }
-        case IETF_STREAM_ID_BLOCKED: {
+        case IETF_STREAMS_BLOCKED_UNIDIRECTIONAL:
+        case IETF_STREAMS_BLOCKED_BIDIRECTIONAL: {
           QuicStreamIdBlockedFrame frame;
-          if (!ProcessStreamIdBlockedFrame(reader, &frame)) {
+          if (!ProcessStreamsBlockedFrame(reader, &frame, frame_type)) {
             return RaiseError(QUIC_STREAM_ID_BLOCKED_DATA);
           }
           QUIC_CODE_COUNT_N(stream_id_blocked_received, 1, 2);
@@ -2927,9 +3226,7 @@
     return false;
   }
 
-  if (GetQuicReloadableFlag(quic_disallow_peer_ack_0) &&
-      largest_acked < first_sending_packet_number_.ToUint64()) {
-    QUIC_RELOADABLE_FLAG_COUNT_N(quic_disallow_peer_ack_0, 1, 3);
+  if (largest_acked < first_sending_packet_number_.ToUint64()) {
     // Connection always sends packet starting from kFirstSendingPacketNumber >
     // 0, peer has observed an unsent packet.
     set_detailed_error("Largest acked is 0.");
@@ -2966,25 +3263,12 @@
   }
 
   if (first_block_length == 0) {
-    // For non-empty ACKs, the first block length must be non-zero.
-    // TODO(fayang): remove this if and return false directly when deprecating
-    // gfe2_reloadable_flag_quic_disallow_peer_ack_0.
-    if (largest_acked != 0 || num_ack_blocks != 0) {
-      set_detailed_error(
-          QuicStrCat("First block length is zero but ACK is "
-                     "not empty. largest acked is ",
-                     largest_acked, ", num ack blocks is ",
-                     QuicTextUtils::Uint64ToString(num_ack_blocks), ".")
-              .c_str());
-      return false;
-    }
-    DCHECK(!GetQuicReloadableFlag(quic_disallow_peer_ack_0));
+    set_detailed_error("First block length is zero.");
+    return false;
   }
   bool first_ack_block_underflow = first_block_length > largest_acked + 1;
-  if (GetQuicReloadableFlag(quic_disallow_peer_ack_0) &&
-      first_block_length + first_sending_packet_number_.ToUint64() >
-          largest_acked + 1) {
-    QUIC_RELOADABLE_FLAG_COUNT_N(quic_disallow_peer_ack_0, 2, 3);
+  if (first_block_length + first_sending_packet_number_.ToUint64() >
+      largest_acked + 1) {
     first_ack_block_underflow = true;
   }
   if (first_ack_block_underflow) {
@@ -3018,10 +3302,8 @@
         return false;
       }
       bool ack_block_underflow = first_received < gap + current_block_length;
-      if (GetQuicReloadableFlag(quic_disallow_peer_ack_0) &&
-          first_received < gap + current_block_length +
+      if (first_received < gap + current_block_length +
                                first_sending_packet_number_.ToUint64()) {
-        QUIC_RELOADABLE_FLAG_COUNT_N(quic_disallow_peer_ack_0, 3, 3);
         ack_block_underflow = true;
       }
       if (ack_block_underflow) {
@@ -3074,6 +3356,14 @@
     return false;
   }
 
+  if (largest_acked.ToUint64() <= delta_from_largest_observed) {
+    set_detailed_error(QuicStrCat("delta_from_largest_observed too high: ",
+                                  delta_from_largest_observed,
+                                  ", largest_acked: ", largest_acked.ToUint64())
+                           .c_str());
+    return false;
+  }
+
   // Time delta from the framer creation.
   uint32_t time_delta_us;
   if (!reader->ReadUInt32(&time_delta_us)) {
@@ -3093,6 +3383,14 @@
       set_detailed_error("Unable to read sequence delta in received packets.");
       return false;
     }
+    if (largest_acked.ToUint64() <= delta_from_largest_observed) {
+      set_detailed_error(
+          QuicStrCat("delta_from_largest_observed too high: ",
+                     delta_from_largest_observed,
+                     ", largest_acked: ", largest_acked.ToUint64())
+              .c_str());
+      return false;
+    }
     seq_num = largest_acked - delta_from_largest_observed;
 
     // Time delta from the previous timestamp.
@@ -3410,11 +3708,6 @@
 
 void QuicFramer::ProcessPaddingFrame(QuicDataReader* reader,
                                      QuicPaddingFrame* frame) {
-  if (version_.transport_version == QUIC_VERSION_35) {
-    frame->num_padding_bytes = reader->BytesRemaining() + 1;
-    reader->ReadRemainingPayload();
-    return;
-  }
   // Type byte has been read.
   frame->num_padding_bytes = 1;
   uint8_t next_byte;
@@ -3429,7 +3722,9 @@
                                      bool no_message_length,
                                      QuicMessageFrame* frame) {
   if (no_message_length) {
-    frame->message_data = QuicString(reader->ReadRemainingPayload());
+    QuicStringPiece remaining(reader->ReadRemainingPayload());
+    frame->data = remaining.data();
+    frame->message_length = remaining.length();
     return true;
   }
 
@@ -3445,7 +3740,8 @@
     return false;
   }
 
-  frame->message_data = QuicString(message_piece);
+  frame->data = message_piece.data();
+  frame->message_length = message_length;
 
   return true;
 }
@@ -3458,14 +3754,18 @@
     QuicConnectionIdLength source_connection_id_length,
     bool includes_version,
     bool includes_diversification_nonce,
-    QuicPacketNumberLength packet_number_length) {
+    QuicPacketNumberLength packet_number_length,
+    QuicVariableLengthIntegerLength retry_token_length_length,
+    uint64_t retry_token_length,
+    QuicVariableLengthIntegerLength length_length) {
   // TODO(ianswett): This is identical to QuicData::AssociatedData.
   return QuicStringPiece(
       encrypted.data(),
       GetStartOfEncryptedData(version, destination_connection_id_length,
                               source_connection_id_length, includes_version,
                               includes_diversification_nonce,
-                              packet_number_length));
+                              packet_number_length, retry_token_length_length,
+                              retry_token_length, length_length));
 }
 
 void QuicFramer::SetDecrypter(EncryptionLevel level,
@@ -3509,7 +3809,7 @@
   DCHECK(packet_number.IsInitialized());
   size_t output_length = 0;
   if (!encrypter_[level]->EncryptPacket(
-          version_.transport_version, packet_number.ToUint64(),
+          packet_number.ToUint64(),
           QuicStringPiece(buffer, ad_len),  // Associated data
           QuicStringPiece(buffer + ad_len, total_len - ad_len),  // Plaintext
           buffer + ad_len,  // Destination buffer
@@ -3538,7 +3838,7 @@
   // Encrypt the plaintext into the buffer.
   size_t output_length = 0;
   if (!encrypter_[level]->EncryptPacket(
-          version_.transport_version, packet_number.ToUint64(), associated_data,
+          packet_number.ToUint64(), associated_data,
           packet.Plaintext(version_.transport_version), buffer + ad_len,
           &output_length, buffer_len - ad_len)) {
     RaiseError(QUIC_ENCRYPTION_FAILURE);
@@ -3548,6 +3848,11 @@
   return ad_len + output_length;
 }
 
+size_t QuicFramer::GetCiphertextSize(EncryptionLevel level,
+                                     size_t plaintext_size) const {
+  return encrypter_[level]->GetCiphertextSize(plaintext_size);
+}
+
 size_t QuicFramer::GetMaxPlaintextSize(size_t ciphertext_size) {
   // In order to keep the code simple, we don't have the current encryption
   // level to hand. Both the NullEncrypter and AES-GCM have a tag length of 12.
@@ -3565,24 +3870,17 @@
   return min_plaintext_size;
 }
 
-bool QuicFramer::DecryptPayload(QuicDataReader* encrypted_reader,
+bool QuicFramer::DecryptPayload(QuicStringPiece encrypted,
+                                QuicStringPiece associated_data,
                                 const QuicPacketHeader& header,
-                                const QuicEncryptedPacket& packet,
                                 char* decrypted_buffer,
                                 size_t buffer_length,
                                 size_t* decrypted_length) {
-  QuicStringPiece encrypted = encrypted_reader->ReadRemainingPayload();
   DCHECK(decrypter_ != nullptr);
-  QuicStringPiece associated_data = GetAssociatedDataFromEncryptedPacket(
-      version_.transport_version, packet,
-      header.destination_connection_id_length,
-      header.source_connection_id_length, header.version_flag,
-      header.nonce != nullptr, header.packet_number_length);
 
   bool success = decrypter_->DecryptPacket(
-      version_.transport_version, header.packet_number.ToUint64(),
-      associated_data, encrypted, decrypted_buffer, decrypted_length,
-      buffer_length);
+      header.packet_number.ToUint64(), associated_data, encrypted,
+      decrypted_buffer, decrypted_length, buffer_length);
   if (success) {
     visitor_->OnDecryptedPacket(decrypter_level_);
   } else if (alternative_decrypter_ != nullptr) {
@@ -3591,7 +3889,7 @@
       alternative_decrypter_->SetDiversificationNonce(*header.nonce);
     }
     bool try_alternative_decryption = true;
-    if (alternative_decrypter_level_ == ENCRYPTION_INITIAL) {
+    if (alternative_decrypter_level_ == ENCRYPTION_ZERO_RTT) {
       if (perspective_ == Perspective::IS_CLIENT) {
         if (header.nonce == nullptr) {
           // Can not use INITIAL decryption without a diversification nonce.
@@ -3604,9 +3902,8 @@
 
     if (try_alternative_decryption) {
       success = alternative_decrypter_->DecryptPacket(
-          version_.transport_version, header.packet_number.ToUint64(),
-          associated_data, encrypted, decrypted_buffer, decrypted_length,
-          buffer_length);
+          header.packet_number.ToUint64(), associated_data, encrypted,
+          decrypted_buffer, decrypted_length, buffer_length);
     }
     if (success) {
       visitor_->OnDecryptedPacket(alternative_decrypter_level_);
@@ -3793,7 +4090,7 @@
     case MESSAGE_FRAME:
       return GetMessageFrameSize(version_.transport_version,
                                  last_frame_in_packet,
-                                 frame.message_frame->message_data.length());
+                                 frame.message_frame->message_length);
     case PADDING_FRAME:
       DCHECK(false);
       return 0;
@@ -3890,14 +4187,16 @@
     case WINDOW_UPDATE_FRAME:
       // Depending on whether there is a stream ID or not, will be either a
       // MAX_STREAM_DATA frame or a MAX_DATA frame.
-      if (frame.window_update_frame->stream_id == 0) {
+      if (frame.window_update_frame->stream_id ==
+          QuicUtils::GetInvalidStreamId(transport_version())) {
         type_byte = IETF_MAX_DATA;
       } else {
         type_byte = IETF_MAX_STREAM_DATA;
       }
       break;
     case BLOCKED_FRAME:
-      if (frame.blocked_frame->stream_id == 0) {
+      if (frame.blocked_frame->stream_id ==
+          QuicUtils::GetInvalidStreamId(transport_version())) {
         type_byte = IETF_BLOCKED;
       } else {
         type_byte = IETF_STREAM_BLOCKED;
@@ -3935,10 +4234,20 @@
       type_byte = IETF_NEW_TOKEN;
       break;
     case MAX_STREAM_ID_FRAME:
-      type_byte = IETF_MAX_STREAM_ID;
+      if (QuicUtils::IsBidirectionalStreamId(
+              frame.max_stream_id_frame.max_stream_id)) {
+        type_byte = IETF_MAX_STREAMS_BIDIRECTIONAL;
+      } else {
+        type_byte = IETF_MAX_STREAMS_UNIDIRECTIONAL;
+      }
       break;
     case STREAM_ID_BLOCKED_FRAME:
-      type_byte = IETF_STREAM_ID_BLOCKED;
+      if (QuicUtils::IsBidirectionalStreamId(
+              frame.max_stream_id_frame.max_stream_id)) {
+        type_byte = IETF_STREAMS_BLOCKED_BIDIRECTIONAL;
+      } else {
+        type_byte = IETF_STREAMS_BLOCKED_UNIDIRECTIONAL;
+      }
       break;
     case PATH_RESPONSE_FRAME:
       type_byte = IETF_PATH_RESPONSE;
@@ -4061,8 +4370,6 @@
   return true;
 }
 
-// TODO(dschinazi) b/120240679 - remove perspective once these flags are
-// deprecated: quic_variable_length_connection_ids_(client|server).
 // static
 bool QuicFramer::AppendIetfConnectionId(
     bool version_flag,
@@ -4070,8 +4377,7 @@
     QuicConnectionIdLength destination_connection_id_length,
     QuicConnectionId source_connection_id,
     QuicConnectionIdLength source_connection_id_length,
-    QuicDataWriter* writer,
-    Perspective perspective) {
+    QuicDataWriter* writer) {
   if (version_flag) {
     // Append connection ID length byte.
     uint8_t dcil = GetConnectionIdLengthValue(destination_connection_id_length);
@@ -4082,11 +4388,11 @@
     }
   }
   if (destination_connection_id_length == PACKET_8BYTE_CONNECTION_ID &&
-      !writer->WriteConnectionId(destination_connection_id, perspective)) {
+      !writer->WriteConnectionId(destination_connection_id)) {
     return false;
   }
   if (source_connection_id_length == PACKET_8BYTE_CONNECTION_ID &&
-      !writer->WriteConnectionId(source_connection_id, perspective)) {
+      !writer->WriteConnectionId(source_connection_id)) {
     return false;
   }
   return true;
@@ -4688,10 +4994,7 @@
 bool QuicFramer::AppendBlockedFrame(const QuicBlockedFrame& frame,
                                     QuicDataWriter* writer) {
   if (version_.transport_version == QUIC_VERSION_99) {
-    // TODO(fkastenholz): This should be converted to use
-    // QuicUtils::GetInvalidStreamId to get the correct invalid stream id value
-    // and not rely on 0.
-    if (frame.stream_id == 0) {
+    if (frame.stream_id == QuicUtils::GetInvalidStreamId(transport_version())) {
       return AppendIetfBlockedFrame(frame, writer);
     }
     return AppendStreamBlockedFrame(frame, writer);
@@ -4705,11 +5008,6 @@
 
 bool QuicFramer::AppendPaddingFrame(const QuicPaddingFrame& frame,
                                     QuicDataWriter* writer) {
-  if (version_.transport_version == QUIC_VERSION_35) {
-    writer->WritePadding();
-    return true;
-  }
-
   if (frame.num_padding_bytes == 0) {
     return false;
   }
@@ -4730,12 +5028,15 @@
   if (!writer->WriteUInt8(type_byte)) {
     return false;
   }
-  if (!last_frame_in_packet &&
-      !writer->WriteVarInt62(frame.message_data.length())) {
+  if (!last_frame_in_packet && !writer->WriteVarInt62(frame.message_length)) {
     return false;
   }
-  return writer->WriteBytes(frame.message_data.data(),
-                            frame.message_data.length());
+  for (const auto& slice : frame.message_data) {
+    if (!writer->WriteBytes(slice.data(), slice.length())) {
+      return false;
+    }
+  }
+  return true;
 }
 
 bool QuicFramer::RaiseError(QuicErrorCode error) {
@@ -4760,11 +5061,6 @@
   return header.long_packet_type == VERSION_NEGOTIATION;
 }
 
-Endianness QuicFramer::endianness() const {
-  return version_.transport_version != QUIC_VERSION_35 ? NETWORK_BYTE_ORDER
-                                                       : HOST_BYTE_ORDER;
-}
-
 bool QuicFramer::StartsWithChlo(QuicStreamId id,
                                 QuicStreamOffset offset) const {
   if (data_producer_ == nullptr) {
@@ -4772,7 +5068,7 @@
     return false;
   }
   char buf[sizeof(kCHLO)];
-  QuicDataWriter writer(sizeof(kCHLO), buf, endianness());
+  QuicDataWriter writer(sizeof(kCHLO), buf);
   if (data_producer_->WriteStreamData(id, offset, sizeof(kCHLO), &writer) !=
       WRITE_SUCCESS) {
     QUIC_BUG << "Failed to write data for stream " << id << " with offset "
@@ -4784,10 +5080,6 @@
          0;
 }
 
-PacketHeaderFormat QuicFramer::GetLastPacketFormat() const {
-  return last_header_form_;
-}
-
 bool QuicFramer::AppendIetfConnectionCloseFrame(
     const QuicConnectionCloseFrame& frame,
     QuicDataWriter* writer) {
@@ -5003,7 +5295,7 @@
 
 bool QuicFramer::ProcessMaxDataFrame(QuicDataReader* reader,
                                      QuicWindowUpdateFrame* frame) {
-  frame->stream_id = 0;
+  frame->stream_id = QuicUtils::GetInvalidStreamId(transport_version());
   if (!reader->ReadVarInt62(&frame->byte_offset)) {
     set_detailed_error("Can not read MAX_DATA byte-offset");
     return false;
@@ -5038,22 +5330,47 @@
   return true;
 }
 
-bool QuicFramer::AppendMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame,
-                                        QuicDataWriter* writer) {
-  if (!writer->WriteVarInt62(frame.max_stream_id)) {
-    set_detailed_error("Can not write MAX_STREAM_ID stream id");
+bool QuicFramer::AppendMaxStreamsFrame(const QuicMaxStreamIdFrame& frame,
+                                       QuicDataWriter* writer) {
+  // Convert from the stream id on which the connection is blocked to a count
+  QuicStreamId stream_count =
+      StreamIdToCount(version_.transport_version, frame.max_stream_id);
+
+  if (!writer->WriteVarInt62(stream_count)) {
+    set_detailed_error("Can not write MAX_STREAMS stream count");
     return false;
   }
   return true;
 }
 
-bool QuicFramer::ProcessMaxStreamIdFrame(QuicDataReader* reader,
-                                         QuicMaxStreamIdFrame* frame) {
-  if (!reader->ReadVarIntStreamId(&frame->max_stream_id)) {
-    set_detailed_error("Can not read MAX_STREAM_ID stream id.");
+bool QuicFramer::ProcessMaxStreamsFrame(QuicDataReader* reader,
+                                        QuicMaxStreamIdFrame* frame,
+                                        uint64_t frame_type) {
+  QuicStreamId received_stream_count;
+  if (!reader->ReadVarIntStreamId(&received_stream_count)) {
+    set_detailed_error("Can not read MAX_STREAMS stream count.");
     return false;
   }
-  return true;
+  // TODO(fkastenholz): handle properly when the STREAMS_BLOCKED
+  // frame is implemented and passed up to the stream ID manager.
+  if (received_stream_count == 0) {
+    set_detailed_error("MAX_STREAMS stream count of 0 not supported.");
+    return false;
+  }
+  // Note that this code assumes that the only possible error that
+  // StreamCountToId can detect is that the stream count is too big or is 0.
+  // Too big is prevented by passing in the minimum of the received count
+  // and the maximum supported count, ensuring that the stream ID is
+  // pegged at the maximum allowed ID.
+  // count==0 is handled above, so that detailed_error_ may be set
+  // properly.
+  return StreamCountToId(
+      std::min(
+          received_stream_count,
+          GetMaxStreamCount((frame_type == IETF_MAX_STREAMS_UNIDIRECTIONAL),
+                            perspective_)),
+      /*unidirectional=*/(frame_type == IETF_MAX_STREAMS_UNIDIRECTIONAL),
+      perspective_, version_.transport_version, &frame->max_stream_id);
 }
 
 bool QuicFramer::AppendIetfBlockedFrame(const QuicBlockedFrame& frame,
@@ -5068,10 +5385,7 @@
 bool QuicFramer::ProcessIetfBlockedFrame(QuicDataReader* reader,
                                          QuicBlockedFrame* frame) {
   // Indicates that it is a BLOCKED frame (as opposed to STREAM_BLOCKED).
-  // TODO(fkastenholz): This should be converted to use
-  // QuicUtils::GetInvalidStreamId to get the correct invalid stream id value
-  // and not rely on 0.
-  frame->stream_id = 0;
+  frame->stream_id = QuicUtils::GetInvalidStreamId(transport_version());
   if (!reader->ReadVarInt62(&frame->offset)) {
     set_detailed_error("Can not read blocked offset.");
     return false;
@@ -5105,23 +5419,58 @@
   return true;
 }
 
-bool QuicFramer::AppendStreamIdBlockedFrame(
+bool QuicFramer::AppendStreamsBlockedFrame(
     const QuicStreamIdBlockedFrame& frame,
     QuicDataWriter* writer) {
-  if (!writer->WriteVarInt62(frame.stream_id)) {
-    set_detailed_error("Can not write STREAM_ID_BLOCKED stream id");
+  // Convert from the stream id on which the connection is blocked to a count
+  QuicStreamId stream_count =
+      StreamIdToCount(version_.transport_version, frame.stream_id);
+
+  if (!writer->WriteVarInt62(stream_count)) {
+    set_detailed_error("Can not write STREAMS_BLOCKED stream count");
     return false;
   }
   return true;
 }
 
-bool QuicFramer::ProcessStreamIdBlockedFrame(QuicDataReader* reader,
-                                             QuicStreamIdBlockedFrame* frame) {
-  if (!reader->ReadVarIntStreamId(&frame->stream_id)) {
-    set_detailed_error("Can not read STREAM_ID_BLOCKED stream id.");
+bool QuicFramer::ProcessStreamsBlockedFrame(QuicDataReader* reader,
+                                            QuicStreamIdBlockedFrame* frame,
+                                            uint64_t frame_type) {
+  QuicStreamId received_stream_count;
+  if (!reader->ReadVarIntStreamId(&received_stream_count)) {
+    set_detailed_error("Can not read STREAMS_BLOCKED stream id.");
     return false;
   }
-  return true;
+  // TODO(fkastenholz): handle properly when the STREAMS_BLOCKED
+  // frame is implemented and passed up to the stream ID manager.
+  if (received_stream_count == 0) {
+    set_detailed_error("STREAMS_BLOCKED stream count 0 not supported.");
+    return false;
+  }
+  // TODO(fkastenholz): handle properly when the STREAMS_BLOCKED
+  // frame is implemented and passed up to the stream ID manager.
+  if (received_stream_count >
+      GetMaxStreamCount((frame_type == IETF_MAX_STREAMS_UNIDIRECTIONAL),
+                        ((perspective_ == Perspective::IS_CLIENT)
+                             ? Perspective::IS_SERVER
+                             : Perspective::IS_CLIENT))) {
+    // If stream count is such that the resulting stream ID would exceed our
+    // implementation limit, generate an error.
+    set_detailed_error(
+        "STREAMS_BLOCKED stream count exceeds implementation limit.");
+    return false;
+  }
+  // Convert the stream count to an ID that can be used.
+  // The STREAMS_BLOCKED frame is a request for more streams
+  // that the peer will initiate. If this node is a client, it
+  // means that the peer is a server, and wants server-initiated
+  // stream IDs.
+  return StreamCountToId(
+      received_stream_count,
+      /*unidirectional=*/(frame_type == IETF_STREAMS_BLOCKED_UNIDIRECTIONAL),
+      (perspective_ == Perspective::IS_CLIENT) ? Perspective::IS_SERVER
+                                               : Perspective::IS_CLIENT,
+      version_.transport_version, &frame->stream_id);
 }
 
 bool QuicFramer::AppendNewConnectionIdFrame(
@@ -5136,7 +5485,7 @@
         "Can not write New Connection ID frame connection ID Length");
     return false;
   }
-  if (!writer->WriteConnectionId(frame.connection_id, perspective_)) {
+  if (!writer->WriteConnectionId(frame.connection_id)) {
     set_detailed_error("Can not write New Connection ID frame connection ID");
     return false;
   }
@@ -5171,8 +5520,7 @@
     return false;
   }
 
-  if (!reader->ReadConnectionId(&frame->connection_id, connection_id_length,
-                                perspective_)) {
+  if (!reader->ReadConnectionId(&frame->connection_id, connection_id_length)) {
     set_detailed_error("Unable to read new connection ID frame connection id.");
     return false;
   }
diff --git a/quic/core/quic_framer.h b/quic/core/quic_framer.h
index 46280c5..76d86b8 100644
--- a/quic/core/quic_framer.h
+++ b/quic/core/quic_framer.h
@@ -112,6 +112,16 @@
   // If OnPacketHeader returns false, framing for this packet will cease.
   virtual bool OnPacketHeader(const QuicPacketHeader& header) = 0;
 
+  // Called when the packet being processed contains multiple IETF QUIC packets,
+  // which is due to there being more data after what is covered by the length
+  // field. |packet| contains the remaining data which can be processed.
+  // Note that this is called when the framer parses the length field, before
+  // it attempts to decrypt the first payload. It is the visitor's
+  // responsibility to buffer the packet and call ProcessPacket on it
+  // after the framer is done parsing the current payload. |packet| does not
+  // own its internal buffer, the visitor should make a copy of it.
+  virtual void OnCoalescedPacket(const QuicEncryptedPacket& packet) = 0;
+
   // Called when a StreamFrame has been parsed.
   virtual bool OnStreamFrame(const QuicStreamFrame& frame) = 0;
 
@@ -304,11 +314,11 @@
   // be generated and calculates the appropriate size.
   static size_t GetWindowUpdateFrameSize(QuicTransportVersion version,
                                          const QuicWindowUpdateFrame& frame);
-  // Size in bytes of all MaxStreamId frame fields.
-  static size_t GetMaxStreamIdFrameSize(QuicTransportVersion version,
-                                        const QuicMaxStreamIdFrame& frame);
-  // Size in bytes of all StreamIdBlocked frame fields.
-  static size_t GetStreamIdBlockedFrameSize(
+  // Size in bytes of all MaxStreams frame fields.
+  static size_t GetMaxStreamsFrameSize(QuicTransportVersion version,
+                                       const QuicMaxStreamIdFrame& frame);
+  // Size in bytes of all StreamsBlocked frame fields.
+  static size_t GetStreamsBlockedFrameSize(
       QuicTransportVersion version,
       const QuicStreamIdBlockedFrame& frame);
   // Size in bytes of all Blocked frame fields.
@@ -362,7 +372,10 @@
       QuicConnectionIdLength source_connection_id_length,
       bool includes_version,
       bool includes_diversification_nonce,
-      QuicPacketNumberLength packet_number_length);
+      QuicPacketNumberLength packet_number_length,
+      QuicVariableLengthIntegerLength retry_token_length_length,
+      uint64_t retry_token_length,
+      QuicVariableLengthIntegerLength length_length);
 
   // Serializes a packet containing |frames| into |buffer|.
   // Returns the length of the packet, which must not be longer than
@@ -370,19 +383,22 @@
   size_t BuildDataPacket(const QuicPacketHeader& header,
                          const QuicFrames& frames,
                          char* buffer,
-                         size_t packet_length);
+                         size_t packet_length,
+                         EncryptionLevel level);
 
   // Serializes a probing packet, which is a padded PING packet. Returns the
   // length of the packet. Returns 0 if it fails to serialize.
   size_t BuildConnectivityProbingPacket(const QuicPacketHeader& header,
                                         char* buffer,
-                                        size_t packet_length);
+                                        size_t packet_length,
+                                        EncryptionLevel level);
 
-  // Serializes an IETF probing packet, which is a padded PING packet.
-  // Returns the length of the packet. Returns 0 if it fails to serialize.
-  size_t BuildIetfConnectivityProbingPacket(const QuicPacketHeader& header,
-                                            char* buffer,
-                                            size_t packet_length);
+  // Serializes a probing packet, which is a padded PING packet. Returns the
+  // length of the packet. Returns 0 if it fails to serialize.
+  size_t BuildConnectivityProbingPacketNew(const QuicPacketHeader& header,
+                                           char* buffer,
+                                           size_t packet_length,
+                                           EncryptionLevel level);
 
   // Serialize a probing packet that uses IETF QUIC's PATH CHALLENGE frame. Also
   // fills the packet with padding.
@@ -390,7 +406,8 @@
                                         char* buffer,
                                         size_t packet_length,
                                         QuicPathFrameBuffer* payload,
-                                        QuicRandom* randomizer);
+                                        QuicRandom* randomizer,
+                                        EncryptionLevel level);
 
   // Serialize a probing response packet that uses IETF QUIC's PATH RESPONSE
   // frame. Also fills the packet with padding if |is_padded| is
@@ -400,7 +417,8 @@
                                  char* buffer,
                                  size_t packet_length,
                                  const QuicDeque<QuicPathFrameBuffer>& payloads,
-                                 const bool is_padded);
+                                 const bool is_padded,
+                                 EncryptionLevel level);
 
   // Returns a new public reset packet.
   static std::unique_ptr<QuicEncryptedPacket> BuildPublicResetPacket(
@@ -426,17 +444,24 @@
   // packet will be set -- but it will be set from version_ not
   // header.versions.
   bool AppendPacketHeader(const QuicPacketHeader& header,
-                          QuicDataWriter* writer);
+                          QuicDataWriter* writer,
+                          size_t* length_field_offset);
   bool AppendIetfHeaderTypeByte(const QuicPacketHeader& header,
                                 QuicDataWriter* writer);
   bool AppendIetfPacketHeader(const QuicPacketHeader& header,
-                              QuicDataWriter* writer);
+                              QuicDataWriter* writer,
+                              size_t* length_field_offset);
+  bool WriteIetfLongHeaderLength(const QuicPacketHeader& header,
+                                 QuicDataWriter* writer,
+                                 size_t length_field_offset,
+                                 EncryptionLevel level);
   bool AppendTypeByte(const QuicFrame& frame,
                       bool last_frame_in_packet,
                       QuicDataWriter* writer);
   bool AppendIetfTypeByte(const QuicFrame& frame,
                           bool last_frame_in_packet,
                           QuicDataWriter* writer);
+  size_t AppendIetfFrames(const QuicFrames& frames, QuicDataWriter* writer);
   bool AppendStreamFrame(const QuicStreamFrame& frame,
                          bool last_frame_in_packet,
                          QuicDataWriter* writer);
@@ -485,6 +510,10 @@
                         char* buffer,
                         size_t buffer_len);
 
+  // Returns the length of the ciphertext that would be generated by encrypting
+  // to plaintext of size |plaintext_size| at the given level.
+  size_t GetCiphertextSize(EncryptionLevel level, size_t plaintext_size) const;
+
   // Returns the maximum length of plaintext that can be encrypted
   // to ciphertext no larger than |ciphertext_size|.
   size_t GetMaxPlaintextSize(size_t ciphertext_size);
@@ -507,28 +536,15 @@
   // Returns true if data with |offset| of stream |id| starts with 'CHLO'.
   bool StartsWithChlo(QuicStreamId id, QuicStreamOffset offset) const;
 
-  // Returns byte order to read/write integers and floating numbers.
-  Endianness endianness() const;
-
   // Returns true if |header| is considered as an stateless reset packet.
   bool IsIetfStatelessResetPacket(const QuicPacketHeader& header) const;
 
-  // Returns header wire format of last received packet.
-  // Please do not use this method.
-  // TODO(fayang): Remove last_header_form_ when deprecating
-  // quic_fix_last_packet_is_ietf_quic flag.
-  PacketHeaderFormat GetLastPacketFormat() const;
-
   void set_validate_flags(bool value) { validate_flags_ = value; }
 
   Perspective perspective() const { return perspective_; }
 
   QuicVersionLabel last_version_label() const { return last_version_label_; }
 
-  void set_last_packet_form(PacketHeaderFormat form) {
-    last_header_form_ = form;
-  }
-
   void set_data_producer(QuicStreamFrameDataProducer* data_producer) {
     data_producer_ = data_producer;
   }
@@ -566,12 +582,6 @@
     size_t num_ack_blocks;
   };
 
-  // The same as BuildDataPacket, but it only builds IETF-format packets.
-  size_t BuildIetfDataPacket(const QuicPacketHeader& header,
-                             const QuicFrames& frames,
-                             char* buffer,
-                             size_t packet_length);
-
   bool ProcessDataPacket(QuicDataReader* reader,
                          QuicPacketHeader* header,
                          const QuicEncryptedPacket& packet,
@@ -590,6 +600,16 @@
   bool ProcessVersionNegotiationPacket(QuicDataReader* reader,
                                        const QuicPacketHeader& header);
 
+  bool MaybeProcessIetfInitialRetryToken(QuicDataReader* encrypted_reader,
+                                         QuicPacketHeader* header);
+
+  void MaybeProcessCoalescedPacket(const QuicDataReader& encrypted_reader,
+                                   uint64_t remaining_bytes_length,
+                                   const QuicPacketHeader& header);
+
+  bool MaybeProcessIetfLength(QuicDataReader* encrypted_reader,
+                              QuicPacketHeader* header);
+
   bool ProcessPublicHeader(QuicDataReader* reader,
                            bool last_packet_is_ietf_quic,
                            QuicPacketHeader* header);
@@ -640,9 +660,9 @@
                            bool no_message_length,
                            QuicMessageFrame* frame);
 
-  bool DecryptPayload(QuicDataReader* encrypted_reader,
+  bool DecryptPayload(QuicStringPiece encrypted,
+                      QuicStringPiece associated_data,
                       const QuicPacketHeader& header,
-                      const QuicEncryptedPacket& packet,
                       char* decrypted_buffer,
                       size_t buffer_length,
                       size_t* decrypted_length);
@@ -703,8 +723,7 @@
       QuicConnectionIdLength destination_connection_id_length,
       QuicConnectionId source_connection_id,
       QuicConnectionIdLength source_connection_id_length,
-      QuicDataWriter* writer,
-      Perspective perspective);
+      QuicDataWriter* writer);
 
   // The Append* methods attempt to write the provided header or frame using the
   // |writer|, and return true if successful.
@@ -789,10 +808,11 @@
   bool ProcessMaxStreamDataFrame(QuicDataReader* reader,
                                  QuicWindowUpdateFrame* frame);
 
-  bool AppendMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame,
-                              QuicDataWriter* writer);
-  bool ProcessMaxStreamIdFrame(QuicDataReader* reader,
-                               QuicMaxStreamIdFrame* frame);
+  bool AppendMaxStreamsFrame(const QuicMaxStreamIdFrame& frame,
+                             QuicDataWriter* writer);
+  bool ProcessMaxStreamsFrame(QuicDataReader* reader,
+                              QuicMaxStreamIdFrame* frame,
+                              uint64_t frame_type);
 
   bool AppendIetfBlockedFrame(const QuicBlockedFrame& frame,
                               QuicDataWriter* writer);
@@ -803,10 +823,12 @@
   bool ProcessStreamBlockedFrame(QuicDataReader* reader,
                                  QuicBlockedFrame* frame);
 
-  bool AppendStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame,
-                                  QuicDataWriter* writer);
-  bool ProcessStreamIdBlockedFrame(QuicDataReader* reader,
-                                   QuicStreamIdBlockedFrame* frame);
+  bool AppendStreamsBlockedFrame(const QuicStreamIdBlockedFrame& frame,
+                                 QuicDataWriter* writer);
+  bool ProcessStreamsBlockedFrame(QuicDataReader* reader,
+                                  QuicStreamIdBlockedFrame* frame,
+                                  uint64_t frame_type);
+
   bool AppendNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame,
                                   QuicDataWriter* writer);
   bool ProcessNewConnectionIdFrame(QuicDataReader* reader,
@@ -845,9 +867,6 @@
   QuicConnectionId last_serialized_connection_id_;
   // The last QUIC version label received.
   QuicVersionLabel last_version_label_;
-  // Format of last received packet header, whether it is Google QUIC, IETF long
-  // header packet or IETF short header packet.
-  PacketHeaderFormat last_header_form_;
   // Version of the protocol being used.
   ParsedQuicVersion version_;
   // This vector contains QUIC versions which we currently support.
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc
index 888c87e..c674896 100644
--- a/quic/core/quic_framer_test.cc
+++ b/quic/core/quic_framer_test.cc
@@ -24,6 +24,7 @@
 #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
 #include "net/third_party/quiche/src/quic/test_tools/simple_data_producer.h"
@@ -83,14 +84,12 @@
   bool SetKey(QuicStringPiece key) override { return true; }
   bool SetNoncePrefix(QuicStringPiece nonce_prefix) override { return true; }
   bool SetIV(QuicStringPiece iv) override { return true; }
-  bool EncryptPacket(QuicTransportVersion version,
-                     uint64_t packet_number,
+  bool EncryptPacket(uint64_t packet_number,
                      QuicStringPiece associated_data,
                      QuicStringPiece plaintext,
                      char* output,
                      size_t* output_length,
                      size_t max_output_length) override {
-    version_ = version;
     packet_number_ = QuicPacketNumber(packet_number);
     associated_data_ = QuicString(associated_data);
     plaintext_ = QuicString(plaintext);
@@ -110,7 +109,6 @@
   QuicStringPiece GetKey() const override { return QuicStringPiece(); }
   QuicStringPiece GetNoncePrefix() const override { return QuicStringPiece(); }
 
-  QuicTransportVersion version_;
   QuicPacketNumber packet_number_;
   QuicString associated_data_;
   QuicString plaintext_;
@@ -129,14 +127,12 @@
   bool SetDiversificationNonce(const DiversificationNonce& key) override {
     return true;
   }
-  bool DecryptPacket(QuicTransportVersion version,
-                     uint64_t packet_number,
+  bool DecryptPacket(uint64_t packet_number,
                      QuicStringPiece associated_data,
                      QuicStringPiece ciphertext,
                      char* output,
                      size_t* output_length,
                      size_t max_output_length) override {
-    version_ = version;
     packet_number_ = QuicPacketNumber(packet_number);
     associated_data_ = QuicString(associated_data);
     ciphertext_ = QuicString(ciphertext);
@@ -150,7 +146,6 @@
   QuicStringPiece GetNoncePrefix() const override { return QuicStringPiece(); }
   // Use a distinct value starting with 0xFFFFFF, which is never used by TLS.
   uint32_t cipher_id() const override { return 0xFFFFFFF2; }
-  QuicTransportVersion version_;
   QuicPacketNumber packet_number_;
   QuicString associated_data_;
   QuicString ciphertext_;
@@ -212,6 +207,15 @@
     return accept_packet_;
   }
 
+  void OnCoalescedPacket(const QuicEncryptedPacket& packet) override {
+    size_t coalesced_data_length = packet.length();
+    char* coalesced_data = new char[coalesced_data_length];
+    memcpy(coalesced_data, packet.data(), coalesced_data_length);
+    coalesced_packets_.push_back(QuicMakeUnique<QuicEncryptedPacket>(
+        coalesced_data, coalesced_data_length,
+        /*owns_buffer=*/true));
+  }
+
   bool OnStreamFrame(const QuicStreamFrame& frame) override {
     ++frame_count_;
     // Save a copy of the data so it is valid after the packet is processed.
@@ -278,7 +282,8 @@
 
   bool OnMessageFrame(const QuicMessageFrame& frame) override {
     ++frame_count_;
-    message_frames_.push_back(QuicMakeUnique<QuicMessageFrame>(frame));
+    message_frames_.push_back(
+        QuicMakeUnique<QuicMessageFrame>(frame.data, frame.message_length));
     return true;
   }
 
@@ -386,6 +391,7 @@
   std::vector<std::unique_ptr<QuicPaddingFrame>> padding_frames_;
   std::vector<std::unique_ptr<QuicPingFrame>> ping_frames_;
   std::vector<std::unique_ptr<QuicMessageFrame>> message_frames_;
+  std::vector<std::unique_ptr<QuicEncryptedPacket>> coalesced_packets_;
   QuicRstStreamFrame rst_stream_frame_;
   QuicConnectionCloseFrame connection_close_frame_;
   QuicApplicationCloseFrame application_close_frame_;
@@ -459,7 +465,6 @@
   }
 
   bool CheckEncryption(QuicPacketNumber packet_number, QuicPacket* packet) {
-    EXPECT_EQ(version_.transport_version, encrypter_->version_);
     if (packet_number != encrypter_->packet_number_) {
       QUIC_LOG(ERROR) << "Encrypted incorrect packet number.  expected "
                       << packet_number
@@ -488,36 +493,53 @@
                        bool includes_diversification_nonce,
                        QuicConnectionIdLength destination_connection_id_length,
                        QuicConnectionIdLength source_connection_id_length) {
-    EXPECT_EQ(version_.transport_version, decrypter_->version_);
+    return CheckDecryption(
+        encrypted, includes_version, includes_diversification_nonce,
+        destination_connection_id_length, source_connection_id_length,
+        VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0);
+  }
+
+  bool CheckDecryption(
+      const QuicEncryptedPacket& encrypted,
+      bool includes_version,
+      bool includes_diversification_nonce,
+      QuicConnectionIdLength destination_connection_id_length,
+      QuicConnectionIdLength source_connection_id_length,
+      QuicVariableLengthIntegerLength retry_token_length_length,
+      size_t retry_token_length,
+      QuicVariableLengthIntegerLength length_length) {
     if (visitor_.header_->packet_number != decrypter_->packet_number_) {
       QUIC_LOG(ERROR) << "Decrypted incorrect packet number.  expected "
                       << visitor_.header_->packet_number
                       << " actual: " << decrypter_->packet_number_;
       return false;
     }
-    if (QuicFramer::GetAssociatedDataFromEncryptedPacket(
+    QuicStringPiece associated_data =
+        QuicFramer::GetAssociatedDataFromEncryptedPacket(
             framer_.transport_version(), encrypted,
             destination_connection_id_length, source_connection_id_length,
             includes_version, includes_diversification_nonce,
-            PACKET_4BYTE_PACKET_NUMBER) != decrypter_->associated_data_) {
+            PACKET_4BYTE_PACKET_NUMBER, retry_token_length_length,
+            retry_token_length, length_length);
+    if (associated_data != decrypter_->associated_data_) {
       QUIC_LOG(ERROR) << "Decrypted incorrect associated data.  expected "
-                      << QuicFramer::GetAssociatedDataFromEncryptedPacket(
-                             framer_.transport_version(), encrypted,
-                             destination_connection_id_length,
-                             source_connection_id_length, includes_version,
-                             includes_diversification_nonce,
-                             PACKET_4BYTE_PACKET_NUMBER)
-                      << " actual: " << decrypter_->associated_data_;
+                      << QuicTextUtils::HexEncode(associated_data)
+                      << " actual: "
+                      << QuicTextUtils::HexEncode(decrypter_->associated_data_);
       return false;
     }
     QuicStringPiece ciphertext(
         encrypted.AsStringPiece().substr(GetStartOfEncryptedData(
             framer_.transport_version(), destination_connection_id_length,
             source_connection_id_length, includes_version,
-            includes_diversification_nonce, PACKET_4BYTE_PACKET_NUMBER)));
+            includes_diversification_nonce, PACKET_4BYTE_PACKET_NUMBER,
+            retry_token_length_length, retry_token_length, length_length)));
     if (ciphertext != decrypter_->ciphertext_) {
       QUIC_LOG(ERROR) << "Decrypted incorrect ciphertext data.  expected "
-                      << ciphertext << " actual: " << decrypter_->ciphertext_;
+                      << QuicTextUtils::HexEncode(ciphertext) << " actual: "
+                      << QuicTextUtils::HexEncode(decrypter_->ciphertext_)
+                      << " associated data: "
+                      << QuicTextUtils::HexEncode(associated_data);
       return false;
     }
     return true;
@@ -609,12 +631,29 @@
     return BuildUnsizedDataPacket(&framer_, header, frames, packet_size);
   }
 
+  // N starts at 1.
+  QuicStreamId GetNthStreamid(QuicTransportVersion transport_version,
+                              Perspective perspective,
+                              bool bidirectional,
+                              int n) {
+    if (bidirectional) {
+      return QuicUtils::GetFirstBidirectionalStreamId(transport_version,
+                                                      perspective) +
+             ((n - 1) * QuicUtils::StreamIdDelta(transport_version));
+    }
+    // Unidirectional
+    return QuicUtils::GetFirstUnidirectionalStreamId(transport_version,
+                                                     perspective) +
+           ((n - 1) * QuicUtils::StreamIdDelta(transport_version));
+  }
+
   test::TestEncrypter* encrypter_;
   test::TestDecrypter* decrypter_;
   ParsedQuicVersion version_;
   QuicTime start_;
   QuicFramer framer_;
   test::TestQuicVisitor visitor_;
+  SimpleBufferAllocator allocator_;
 };
 
 // Multiple test cases of QuicFramerTest use byte arrays to define packets for
@@ -626,7 +665,7 @@
       GetQuicVersionDigitOnes()
 
 // Run all framer tests with all supported versions of QUIC.
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     QuicFramerTests,
     QuicFramerTest,
     ::testing::ValuesIn(AllSupportedVersionsIncludingTls()));
@@ -764,7 +803,7 @@
     // packet number
     0x78, 0x56, 0x34, 0x12,
   };
-  unsigned char packet47[kMaxPacketSize + 1] = {
+  unsigned char packet46[kMaxPacketSize + 1] = {
     // type (short header 4 byte packet number)
     0x43,
     // connection_id
@@ -775,9 +814,9 @@
   // clang-format on
   unsigned char* p = packet;
   size_t p_size = QUIC_ARRAYSIZE(packet);
-  if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
-    p_size = QUIC_ARRAYSIZE(packet47);
+  if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
+    p_size = QUIC_ARRAYSIZE(packet46);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
@@ -786,7 +825,8 @@
   const size_t header_size = GetPacketHeaderSize(
       framer_.transport_version(), PACKET_8BYTE_CONNECTION_ID,
       PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion,
-      !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER);
+      !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER,
+      VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0);
 
   memset(p + header_size, 0, kMaxPacketSize - header_size);
 
@@ -802,20 +842,12 @@
 }
 
 TEST_P(QuicFramerTest, PacketHeader) {
-  // clang-format off
-  PacketFragments packet38 = {
-      // public flags (8 byte connection_id)
-      {"Unable to read public flags.",
-       {0x28}},
-      // connection_id
-      {"Unable to read ConnectionId.",
-       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
-      // packet number
-      {"Unable to read packet number.",
-       {0x78, 0x56, 0x34, 0x12}},
-  };
+  if (framer_.transport_version() > QUIC_VERSION_43) {
+    return;
+  }
 
-  PacketFragments packet39 = {
+  // clang-format off
+  PacketFragments packet = {
       // public flags (8 byte connection_id)
       {"Unable to read public flags.",
        {0x28}},
@@ -828,12 +860,7 @@
   };
   // clang-format on
 
-  if (framer_.transport_version() > QUIC_VERSION_43) {
-    return;
-  }
-
-  PacketFragments& fragments =
-      framer_.transport_version() == QUIC_VERSION_35 ? packet38 : packet39;
+  PacketFragments& fragments = packet;
 
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
@@ -869,7 +896,7 @@
     {"Unable to read packet number.",
      {0x12, 0x34, 0x56, 0x78}},
   };
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
     // type (long header with packet type INITIAL)
     {"Unable to read type.",
      {0xC3}},
@@ -888,12 +915,13 @@
   };
   // clang-format on
 
-  if (framer_.transport_version() <= QUIC_VERSION_43) {
+  if (framer_.transport_version() <= QUIC_VERSION_43 ||
+      QuicVersionHasLongHeaderLengths(framer_.transport_version())) {
     return;
   }
 
   PacketFragments& fragments =
-      framer_.transport_version() > QUIC_VERSION_46 ? packet47 : packet44;
+      framer_.transport_version() > QUIC_VERSION_44 ? packet46 : packet44;
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
 
@@ -907,7 +935,7 @@
   EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number);
 
   CheckFramingBoundaries(
-      framer_.transport_version() > QUIC_VERSION_46 ? packet47 : packet44,
+      framer_.transport_version() > QUIC_VERSION_44 ? packet46 : packet44,
       QUIC_INVALID_PACKET_HEADER);
 }
 
@@ -924,16 +952,6 @@
       // connection_id
       // packet number
       {"Unable to read packet number.",
-       {0x78, 0x56, 0x34, 0x12}}
-  };
-
-  PacketFragments packet39 = {
-      // public flags (0 byte connection_id)
-      {"Unable to read public flags.",
-       {0x20}},
-      // connection_id
-      // packet number
-      {"Unable to read packet number.",
        {0x12, 0x34, 0x56, 0x78}},
   };
 
@@ -947,7 +965,7 @@
          {0x12, 0x34, 0x56, 0x78}},
    };
 
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
         // type (short header, 4 byte packet number)
         {"Unable to read type.",
          {0x43}},
@@ -959,12 +977,9 @@
   // clang-format on
 
   PacketFragments& fragments =
-      framer_.transport_version() > QUIC_VERSION_46
-          ? packet47
-          : (framer_.transport_version() > QUIC_VERSION_43
-                 ? packet44
-                 : (framer_.transport_version() == QUIC_VERSION_35 ? packet
-                                                                   : packet39));
+      framer_.transport_version() > QUIC_VERSION_44
+          ? packet46
+          : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet);
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
   EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
@@ -993,21 +1008,6 @@
        {QUIC_VERSION_BYTES}},
       // packet number
       {"Unable to read packet number.",
-       {0x78, 0x56, 0x34, 0x12}},
-  };
-
-  PacketFragments packet39 = {
-      // public flags (0 byte connection_id)
-      {"Unable to read public flags.",
-       {0x29}},
-      // connection_id
-      {"Unable to read ConnectionId.",
-       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
-      // version tag
-      {"Unable to read protocol version.",
-       {QUIC_VERSION_BYTES}},
-      // packet number
-      {"Unable to read packet number.",
        {0x12, 0x34, 0x56, 0x78}},
   };
 
@@ -1029,7 +1029,7 @@
        {0x12, 0x34, 0x56, 0x78}},
   };
 
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
       // type (long header with packet type ZERO_RTT_PROTECTED and 4 bytes
       // packet number)
       {"Unable to read type.",
@@ -1047,15 +1047,37 @@
       {"Unable to read packet number.",
        {0x12, 0x34, 0x56, 0x78}},
   };
+
+  PacketFragments packet99 = {
+      // type (long header with packet type ZERO_RTT_PROTECTED and 4 bytes
+      // packet number)
+      {"Unable to read type.",
+       {0xD3}},
+      // version tag
+      {"Unable to read protocol version.",
+       {QUIC_VERSION_BYTES}},
+      // connection_id length
+      {"Unable to read ConnectionId length.",
+       {0x50}},
+      // connection_id
+      {"Unable to read Destination ConnectionId.",
+       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+      // long header packet length
+      {"Unable to read long header payload length.",
+       {0x04}},
+      // packet number
+      {"Long header payload length longer than packet.",
+       {0x12, 0x34, 0x56, 0x78}},
+  };
   // clang-format on
 
   PacketFragments& fragments =
-      framer_.transport_version() > QUIC_VERSION_46
-          ? packet47
-          : (framer_.transport_version() > QUIC_VERSION_43
-                 ? packet44
-                 : (framer_.transport_version() == QUIC_VERSION_35 ? packet
-                                                                   : packet39));
+      framer_.transport_version() == QUIC_VERSION_99
+          ? packet99
+          : framer_.transport_version() > QUIC_VERSION_44
+                ? packet46
+                : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+                                                                 : packet);
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
   EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
@@ -1084,18 +1106,6 @@
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"Unable to read packet number.",
-       {0x78, 0x56, 0x34, 0x12}},
-  };
-
-  PacketFragments packet39 = {
-      // public flags (8 byte connection_id and 4 byte packet number)
-      {"Unable to read public flags.",
-       {0x28}},
-      // connection_id
-      {"Unable to read ConnectionId.",
-       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
-      // packet number
-      {"Unable to read packet number.",
        {0x12, 0x34, 0x56, 0x78}},
   };
 
@@ -1111,7 +1121,7 @@
        {0x12, 0x34, 0x56, 0x78}},
   };
 
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
       // type (short header, 4 byte packet number)
       {"Unable to read type.",
        {0x43}},
@@ -1125,12 +1135,9 @@
   // clang-format on
 
   PacketFragments& fragments =
-      framer_.transport_version() > QUIC_VERSION_46
-          ? packet47
-          : (framer_.transport_version() > QUIC_VERSION_43
-                 ? packet44
-                 : (framer_.transport_version() == QUIC_VERSION_35 ? packet
-                                                                   : packet39));
+      framer_.transport_version() > QUIC_VERSION_44
+          ? packet46
+          : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet);
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
   EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
@@ -1158,18 +1165,6 @@
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"Unable to read packet number.",
-       {0x78, 0x56}},
-  };
-
-  PacketFragments packet39 = {
-      // public flags (8 byte connection_id and 2 byte packet number)
-      {"Unable to read public flags.",
-       {0x18}},
-      // connection_id
-      {"Unable to read ConnectionId.",
-       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
-      // packet number
-      {"Unable to read packet number.",
        {0x56, 0x78}},
   };
 
@@ -1185,7 +1180,7 @@
        {0x56, 0x78}},
   };
 
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
       // type (short header, 2 byte packet number)
       {"Unable to read type.",
        {0x41}},
@@ -1199,12 +1194,9 @@
   // clang-format on
 
   PacketFragments& fragments =
-      framer_.transport_version() > QUIC_VERSION_46
-          ? packet47
-          : (framer_.transport_version() > QUIC_VERSION_43
-                 ? packet44
-                 : (framer_.transport_version() == QUIC_VERSION_35 ? packet
-                                                                   : packet39));
+      framer_.transport_version() > QUIC_VERSION_44
+          ? packet46
+          : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet);
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
   EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
@@ -1248,7 +1240,7 @@
        {0x78}},
   };
 
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
       // type (8 byte connection_id and 1 byte packet number)
       {"Unable to read type.",
        {0x40}},
@@ -1263,8 +1255,8 @@
   // clang-format on
 
   PacketFragments& fragments =
-      framer_.transport_version() > QUIC_VERSION_46
-          ? packet47
+      framer_.transport_version() > QUIC_VERSION_44
+          ? packet46
           : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet);
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
@@ -1360,24 +1352,6 @@
     0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
     0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // frame type (padding)
-    0x00,
-    0x00, 0x00, 0x00, 0x00
-  };
-
-  unsigned char packet39[] = {
-    // public flags: includes nonce flag
-    0x2C,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // nonce
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
-    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
-    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // frame type (padding)
@@ -1407,7 +1381,7 @@
     0x00, 0x00, 0x00, 0x00
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type: Long header with packet type ZERO_RTT_PROTECTED and 1 byte packet
     // number.
     0xD0,
@@ -1429,18 +1403,48 @@
     0x00,
     0x00, 0x00, 0x00, 0x00
   };
+
+  unsigned char packet99[] = {
+    // type: Long header with packet type ZERO_RTT_PROTECTED and 1 byte packet
+    // number.
+    0xD0,
+    // version tag
+    QUIC_VERSION_BYTES,
+    // connection_id length
+    0x05,
+    // connection_id
+    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+    // long header packet length
+    0x26,
+    // packet number
+    0x78,
+    // nonce
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+
+    // frame type (padding)
+    0x00,
+    0x00, 0x00, 0x00, 0x00
+  };
   // clang-format on
 
+  if (framer_.version().handshake_protocol != PROTOCOL_QUIC_CRYPTO) {
+    return;
+  }
+
   unsigned char* p = packet;
   size_t p_size = QUIC_ARRAYSIZE(packet);
-  if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
-    p_size = QUIC_ARRAYSIZE(packet47);
+  if (framer_.transport_version() == QUIC_VERSION_99) {
+    p = packet99;
+    p_size = QUIC_ARRAYSIZE(packet99);
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
+    p_size = QUIC_ARRAYSIZE(packet46);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   QuicEncryptedPacket encrypted(AsChars(p), p_size, false);
@@ -1452,7 +1456,7 @@
   }
   EXPECT_EQ(1u, visitor_.padding_frames_.size());
   EXPECT_EQ(5, visitor_.padding_frames_[0]->num_padding_bytes);
-};
+}
 
 TEST_P(QuicFramerTest, LargePublicFlagWithMismatchedVersions) {
   // clang-format off
@@ -1464,21 +1468,6 @@
     // version tag
     'Q', '0', '0', '0',
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // frame type (padding frame)
-    0x00,
-    0x00, 0x00, 0x00, 0x00
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id, version flag and an unknown flag)
-    0x29,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // version tag
-    'Q', '0', '0', '0',
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // frame type (padding frame)
@@ -1505,11 +1494,8 @@
   // clang-format on
 
   QuicEncryptedPacket encrypted(
-      AsChars(framer_.transport_version() > QUIC_VERSION_43
-                  ? packet44
-                  : (framer_.transport_version() == QUIC_VERSION_35
-                         ? packet
-                         : packet39)),
+      AsChars(framer_.transport_version() > QUIC_VERSION_43 ? packet44
+                                                            : packet),
       framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44)
                                                     : QUIC_ARRAYSIZE(packet),
       false);
@@ -1520,7 +1506,7 @@
   EXPECT_EQ(1, visitor_.version_mismatch_);
   EXPECT_EQ(1u, visitor_.padding_frames_.size());
   EXPECT_EQ(5, visitor_.padding_frames_[0]->num_padding_bytes);
-};
+}
 
 TEST_P(QuicFramerTest, PaddingFrame) {
   // clang-format off
@@ -1530,88 +1516,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // frame type (padding frame)
-    0x00,
-    // Ignored data (which in this case is a stream frame)
-    // frame type (stream frame with fin)
-    0xFF,
-    // stream id
-    0x04, 0x03, 0x02, 0x01,
-    // offset
-    0x54, 0x76, 0x10, 0x32,
-    0xDC, 0xFE, 0x98, 0x3A,
-    // data length
-    0x0c, 0x00,
-    // data
-    'h',  'e',  'l',  'l',
-    'o',  ' ',  'w',  'o',
-    'r',  'l',  'd',  '!',
-  };
-  // clang-format on
-
-  if (framer_.transport_version() != QUIC_VERSION_35) {
-    return;
-  }
-
-  QuicEncryptedPacket encrypted(AsChars(packet), QUIC_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, !kIncludeDiversificationNonce,
-      PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
-
-  ASSERT_EQ(0u, visitor_.stream_frames_.size());
-  EXPECT_EQ(0u, visitor_.ack_frames_.size());
-  EXPECT_EQ(1u, visitor_.padding_frames_.size());
-  EXPECT_EQ(28, visitor_.padding_frames_[0]->num_padding_bytes);
-  // A packet with no frames is not acceptable.
-  CheckProcessingFails(
-      packet,
-      GetPacketHeaderSize(
-          framer_.transport_version(), PACKET_8BYTE_CONNECTION_ID,
-          PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion,
-          !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER),
-      "Packet has no frames.", QUIC_MISSING_PAYLOAD);
-}
-
-TEST_P(QuicFramerTest, NewPaddingFrame) {
-  // clang-format off
-  unsigned char packet[] = {
-    // public flags (8 byte connection_id)
-    0x28,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // paddings
-    0x00, 0x00,
-    // frame type (stream frame with fin)
-    0xFF,
-    // stream id
-    0x04, 0x03, 0x02, 0x01,
-    // offset
-    0x54, 0x76, 0x10, 0x32,
-    0xDC, 0xFE, 0x98, 0x3A,
-    // data length
-    0x0c, 0x00,
-    // data
-    'h',  'e',  'l',  'l',
-    'o',  ' ',  'w',  'o',
-    'r',  'l',  'd',  '!',
-    // paddings
-    0x00, 0x00,
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id)
-    0x28,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // paddings
@@ -1660,7 +1564,7 @@
     0x00, 0x00,
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short header, 4 byte packet number)
     0x43,
     // connection_id
@@ -1716,22 +1620,17 @@
   };
   // clang-format on
 
-  if (framer_.transport_version() == QUIC_VERSION_35) {
-    return;
-  }
   unsigned char* p = packet;
   size_t p_size = QUIC_ARRAYSIZE(packet);
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
     p_size = QUIC_ARRAYSIZE(packet99);
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
-    p_size = QUIC_ARRAYSIZE(packet47);
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
+    p_size = QUIC_ARRAYSIZE(packet46);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   QuicEncryptedPacket encrypted(AsChars(p), p_size, false);
@@ -1764,36 +1663,6 @@
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"",
-       {0x78, 0x56, 0x34, 0x12}},
-      // frame type (stream frame with fin)
-      {"",
-       {0xFF}},
-      // stream id
-      {"Unable to read stream_id.",
-       {0x04, 0x03, 0x02, 0x01}},
-      // offset
-      {"Unable to read offset.",
-       {0x54, 0x76, 0x10, 0x32,
-        0xDC, 0xFE, 0x98, 0x3A}},
-      {"Unable to read frame data.",
-       {
-         // data length
-         0x0c, 0x00,
-         // data
-         'h',  'e',  'l',  'l',
-         'o',  ' ',  'w',  'o',
-         'r',  'l',  'd',  '!'}},
-  };
-
-  PacketFragments packet39 = {
-      // public flags (8 byte connection_id)
-      {"",
-       {0x28}},
-      // connection_id
-      {"",
-       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
-      // packet number
-      {"",
        {0x12, 0x34, 0x56, 0x78}},
       // frame type (stream frame with fin)
       {"",
@@ -1845,7 +1714,7 @@
          'r',  'l',  'd',  '!'}},
   };
 
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
       // type (short header, 4 byte packet number)
       {"",
        {0x43}},
@@ -1909,13 +1778,10 @@
   PacketFragments& fragments =
       framer_.transport_version() == QUIC_VERSION_99
           ? packet99
-          : (framer_.transport_version() > QUIC_VERSION_46
-                 ? packet47
-                 : (framer_.transport_version() > QUIC_VERSION_43
-                        ? packet44
-                        : (framer_.transport_version() != QUIC_VERSION_35
-                               ? packet39
-                               : packet)));
+          : (framer_.transport_version() > QUIC_VERSION_44
+                 ? packet46
+                 : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+                                                                  : packet));
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
   EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
@@ -1996,33 +1862,10 @@
                        QuicMakeUnique<NullDecrypter>(Perspective::IS_CLIENT));
   decrypter_ = new test::TestDecrypter();
   framer_.SetAlternativeDecrypter(
-      ENCRYPTION_INITIAL, std::unique_ptr<QuicDecrypter>(decrypter_), false);
+      ENCRYPTION_ZERO_RTT, std::unique_ptr<QuicDecrypter>(decrypter_), false);
 
   // clang-format off
   unsigned char packet[] = {
-      // public flags (8 byte connection_id)
-      0x28,
-      // connection_id
-      0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE,
-      // packet number
-      0x78, 0x56, 0x34, 0x12,
-
-      // frame type (stream frame with fin)
-      0xFF,
-      // stream id
-      0x04, 0x03, 0x02, 0x01,
-      // offset
-      0x54, 0x76, 0x10, 0x32,
-      0xDC, 0xFE, 0x98, 0x3A,
-      // data length
-      0x0c, 0x00,
-      // data
-      'h',  'e',  'l',  'l',
-      'o',  ' ',  'w',  'o',
-      'r',  'l',  'd',  '!',
-  };
-
-  unsigned char packet39[] = {
         // public flags (8 byte connection_id)
         0x28,
         // connection_id
@@ -2076,8 +1919,6 @@
   unsigned char* p = packet;
   if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
   QuicEncryptedPacket encrypted(AsChars(p),
                                 framer_.transport_version() > QUIC_VERSION_43
@@ -2108,36 +1949,6 @@
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"",
-       {0x78, 0x56, 0x34, 0x12}},
-      // frame type (stream frame with fin)
-      {"",
-       {0xFE}},
-      // stream id
-      {"Unable to read stream_id.",
-       {0x04, 0x03, 0x02}},
-      // offset
-      {"Unable to read offset.",
-       {0x54, 0x76, 0x10, 0x32,
-        0xDC, 0xFE, 0x98, 0x3A}},
-      {"Unable to read frame data.",
-       {
-         // data length
-         0x0c, 0x00,
-         // data
-         'h',  'e',  'l',  'l',
-         'o',  ' ',  'w',  'o',
-         'r',  'l',  'd',  '!'}},
-  };
-
-  PacketFragments packet39 = {
-      // public flags (8 byte connection_id)
-      {"",
-       {0x28}},
-      // connection_id
-      {"",
-       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
-      // packet number
-      {"",
        {0x12, 0x34, 0x56, 0x78}},
       // frame type (stream frame with fin)
       {"",
@@ -2160,8 +1971,7 @@
   };
   // clang-format on
 
-  PacketFragments& fragments =
-      framer_.transport_version() != QUIC_VERSION_35 ? packet39 : packet;
+  PacketFragments& fragments = packet;
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
   EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
@@ -2194,36 +2004,6 @@
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"",
-       {0x78, 0x56, 0x34, 0x12}},
-      // frame type (stream frame with fin)
-      {"",
-       {0xFD}},
-      // stream id
-      {"Unable to read stream_id.",
-       {0x04, 0x03}},
-      // offset
-      {"Unable to read offset.",
-       {0x54, 0x76, 0x10, 0x32,
-        0xDC, 0xFE, 0x98, 0x3A}},
-      {"Unable to read frame data.",
-       {
-         // data length
-         0x0c, 0x00,
-         // data
-         'h',  'e',  'l',  'l',
-         'o',  ' ',  'w',  'o',
-         'r',  'l',  'd',  '!'}},
-  };
-
-  PacketFragments packet39 = {
-      // public flags (8 byte connection_id)
-      {"",
-       {0x28}},
-      // connection_id
-      {"",
-       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
-      // packet number
-      {"",
        {0x12, 0x34, 0x56, 0x78}},
       // frame type (stream frame with fin)
       {"",
@@ -2275,7 +2055,7 @@
           'r',  'l',  'd',  '!'}},
   };
 
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
       // type (short header, 4 byte packet number)
       {"",
        {0x43}},
@@ -2339,13 +2119,10 @@
   PacketFragments& fragments =
       framer_.transport_version() == QUIC_VERSION_99
           ? packet99
-          : (framer_.transport_version() > QUIC_VERSION_46
-                 ? packet47
-                 : (framer_.transport_version() > QUIC_VERSION_43
-                        ? packet44
-                        : (framer_.transport_version() != QUIC_VERSION_35
-                               ? packet39
-                               : packet)));
+          : (framer_.transport_version() > QUIC_VERSION_44
+                 ? packet46
+                 : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+                                                                  : packet));
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
   EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
@@ -2378,36 +2155,6 @@
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"",
-       {0x78, 0x56, 0x34, 0x12}},
-      // frame type (stream frame with fin)
-      {"",
-       {0xFC}},
-      // stream id
-      {"Unable to read stream_id.",
-       {0x04}},
-      // offset
-      {"Unable to read offset.",
-       {0x54, 0x76, 0x10, 0x32,
-        0xDC, 0xFE, 0x98, 0x3A}},
-      {"Unable to read frame data.",
-       {
-         // data length
-         0x0c, 0x00,
-         // data
-         'h',  'e',  'l',  'l',
-         'o',  ' ',  'w',  'o',
-         'r',  'l',  'd',  '!'}},
-  };
-
-  PacketFragments packet39 = {
-      // public flags (8 byte connection_id)
-      {"",
-       {0x28}},
-      // connection_id
-      {"",
-       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
-      // packet number
-      {"",
        {0x12, 0x34, 0x56, 0x78}},
       // frame type (stream frame with fin)
       {"",
@@ -2459,7 +2206,7 @@
          'r',  'l',  'd',  '!'}},
   };
 
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
       // type (short header, 4 byte packet number)
       {"",
        {0x43}},
@@ -2523,13 +2270,10 @@
   PacketFragments& fragments =
       framer_.transport_version() == QUIC_VERSION_99
           ? packet99
-          : (framer_.transport_version() > QUIC_VERSION_46
-                 ? packet47
-                 : (framer_.transport_version() > QUIC_VERSION_43
-                        ? packet44
-                        : (framer_.transport_version() != QUIC_VERSION_35
-                               ? packet39
-                               : packet)));
+          : (framer_.transport_version() > QUIC_VERSION_44
+                 ? packet46
+                 : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+                                                                  : packet));
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
   EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
@@ -2565,39 +2309,6 @@
        {QUIC_VERSION_BYTES}},
       // packet number
       {"",
-       {0x78, 0x56, 0x34, 0x12}},
-      // frame type (stream frame with fin)
-      {"",
-       {0xFE}},
-      // stream id
-      {"Unable to read stream_id.",
-       {0x04, 0x03, 0x02}},
-      // offset
-      {"Unable to read offset.",
-       {0x54, 0x76, 0x10, 0x32,
-        0xDC, 0xFE, 0x98, 0x3A}},
-      {"Unable to read frame data.",
-       {
-         // data length
-         0x0c, 0x00,
-         // data
-         'h',  'e',  'l',  'l',
-         'o',  ' ',  'w',  'o',
-         'r',  'l',  'd',  '!'}},
-  };
-
-  PacketFragments packet39 = {
-      // public flags (version, 8 byte connection_id)
-      {"",
-       {0x29}},
-      // connection_id
-      {"",
-       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
-      // version tag
-      {"",
-       {QUIC_VERSION_BYTES}},
-      // packet number
-      {"",
        {0x12, 0x34, 0x56, 0x78}},
       // frame type (stream frame with fin)
       {"",
@@ -2655,7 +2366,7 @@
          'r',  'l',  'd',  '!'}},
   };
 
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
       // public flags (long header with packet type ZERO_RTT_PROTECTED and
       // 4-byte packet number)
       {"",
@@ -2706,6 +2417,9 @@
       // connection_id
       {"",
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+      // long header packet length
+      {"",
+       {0x1E}},
       // packet number
       {"",
        {0x12, 0x34, 0x56, 0x78}},
@@ -2713,33 +2427,38 @@
       {"",
        {0x08 | 0x01 | 0x02 | 0x04}},
       // stream id
-      {"Unable to read stream_id.",
+      {"Long header payload length longer than packet.",
        {kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04}},
       // offset
-      {"Unable to read stream data offset.",
+      {"Long header payload length longer than packet.",
        {kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
         0x32, 0x10, 0x76, 0x54}},
       // data length
-      {"Unable to read stream data length.",
+      {"Long header payload length longer than packet.",
        {kVarInt62OneByte + 0x0c}},
       // data
-      {"Unable to read frame data.",
+      {"Long header payload length longer than packet.",
        { 'h',  'e',  'l',  'l',
          'o',  ' ',  'w',  'o',
          'r',  'l',  'd',  '!'}},
   };
   // clang-format on
 
+  QuicVariableLengthIntegerLength retry_token_length_length =
+      VARIABLE_LENGTH_INTEGER_LENGTH_0;
+  size_t retry_token_length = 0;
+  QuicVariableLengthIntegerLength length_length =
+      QuicVersionHasLongHeaderLengths(framer_.transport_version())
+          ? VARIABLE_LENGTH_INTEGER_LENGTH_1
+          : VARIABLE_LENGTH_INTEGER_LENGTH_0;
+
   PacketFragments& fragments =
       framer_.transport_version() == QUIC_VERSION_99
           ? packet99
-          : (framer_.transport_version() > QUIC_VERSION_46
-                 ? packet47
-                 : (framer_.transport_version() > QUIC_VERSION_43
-                        ? packet44
-                        : (framer_.transport_version() != QUIC_VERSION_35
-                               ? packet39
-                               : packet)));
+          : (framer_.transport_version() > QUIC_VERSION_44
+                 ? packet46
+                 : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+                                                                  : packet));
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
   EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
@@ -2748,7 +2467,8 @@
   ASSERT_TRUE(visitor_.header_.get());
   EXPECT_TRUE(CheckDecryption(
       *encrypted, kIncludeVersion, !kIncludeDiversificationNonce,
-      PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+      PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID,
+      retry_token_length_length, retry_token_length, length_length));
 
   ASSERT_EQ(1u, visitor_.stream_frames_.size());
   EXPECT_EQ(0u, visitor_.ack_frames_.size());
@@ -2758,7 +2478,10 @@
   EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset);
   CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get());
 
-  CheckFramingBoundaries(fragments, QUIC_INVALID_STREAM_DATA);
+  CheckFramingBoundaries(fragments,
+                         framer_.transport_version() == QUIC_VERSION_99
+                             ? QUIC_INVALID_PACKET_HEADER
+                             : QUIC_INVALID_STREAM_DATA);
 }
 
 TEST_P(QuicFramerTest, RejectPacket) {
@@ -2771,29 +2494,6 @@
       // connection_id
       0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
       // packet number
-      0x78, 0x56, 0x34, 0x12,
-
-      // frame type (stream frame with fin)
-      0xFF,
-      // stream id
-      0x04, 0x03, 0x02, 0x01,
-      // offset
-      0x54, 0x76, 0x10, 0x32,
-      0xDC, 0xFE, 0x98, 0x3A,
-      // data length
-      0x0c, 0x00,
-      // data
-      'h',  'e',  'l',  'l',
-      'o',  ' ',  'w',  'o',
-      'r',  'l',  'd',  '!',
-  };
-
-  unsigned char packet39[] = {
-      // public flags (8 byte connection_id)
-      0x28,
-      // connection_id
-      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-      // packet number
       0x12, 0x34, 0x56, 0x78,
 
       // frame type (stream frame with fin)
@@ -2834,7 +2534,7 @@
       'r',  'l',  'd',  '!',
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
       // type (short header, 4 byte packet number)
       0x43,
       // connection_id
@@ -2859,12 +2559,10 @@
   // clang-format on
 
   unsigned char* p = packet;
-  if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
+  if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
   QuicEncryptedPacket encrypted(AsChars(p),
                                 framer_.transport_version() > QUIC_VERSION_43
@@ -2903,7 +2601,7 @@
     0x01,
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short header, 1 byte packet number)
     0x40,
     // connection_id
@@ -2914,12 +2612,12 @@
   // clang-format on
 
   QuicEncryptedPacket encrypted(
-      framer_.transport_version() > QUIC_VERSION_46
-          ? AsChars(packet47)
+      framer_.transport_version() > QUIC_VERSION_44
+          ? AsChars(packet46)
           : (framer_.transport_version() > QUIC_VERSION_43 ? AsChars(packet44)
                                                            : AsChars(packet)),
-      framer_.transport_version() > QUIC_VERSION_46
-          ? QUIC_ARRAYSIZE(packet47)
+      framer_.transport_version() > QUIC_VERSION_44
+          ? QUIC_ARRAYSIZE(packet46)
           : (framer_.transport_version() > QUIC_VERSION_43
                  ? QUIC_ARRAYSIZE(packet44)
                  : QUIC_ARRAYSIZE(packet)),
@@ -2942,34 +2640,6 @@
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"",
-       {0x78, 0x56, 0x34, 0x12}},
-      // frame type (ack frame)
-      // (one ack block, 2 byte largest observed, 2 byte block length)
-      {"",
-       {0x45}},
-      // largest acked
-      {"Unable to read largest acked.",
-       {0x34, 0x12}},
-      // Zero delta time.
-      {"Unable to read ack delay time.",
-       {0x00, 0x00}},
-      // first ack block length.
-      {"Unable to read first ack block length.",
-       {0x34, 0x12}},
-      // num timestamps.
-      {"Unable to read num received packets.",
-       {0x00}}
-  };
-
-  PacketFragments packet39 = {
-      // public flags (8 byte connection_id)
-      {"",
-       {0x2C}},
-      // connection_id
-      {"",
-       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
-      // packet number
-      {"",
        {0x12, 0x34, 0x56, 0x78}},
       // frame type (ack frame)
       // (one ack block, 2 byte largest observed, 2 byte block length)
@@ -3017,7 +2687,7 @@
        {0x00}}
   };
 
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
       // type (short packet, 4 byte packet number)
       {"",
        {0x43}},
@@ -3084,13 +2754,10 @@
   PacketFragments& fragments =
       framer_.transport_version() == QUIC_VERSION_99
           ? packet99
-          : (framer_.transport_version() > QUIC_VERSION_46
-                 ? packet47
-                 : (framer_.transport_version() > QUIC_VERSION_43
-                        ? packet44
-                        : (framer_.transport_version() != QUIC_VERSION_35
-                               ? packet39
-                               : packet)));
+          : (framer_.transport_version() > QUIC_VERSION_44
+                 ? packet46
+                 : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+                                                                  : packet));
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
   EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
@@ -3124,34 +2791,6 @@
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"",
-       {0x78, 0x56, 0x34, 0x12}},
-      // frame type (ack frame)
-      // (one ack block, 2 byte largest observed, 2 byte block length)
-      {"",
-       {0x45}},
-      // largest acked
-      {"Unable to read largest acked.",
-       {0x34, 0x12}},
-      // Zero delta time.
-      {"Unable to read ack delay time.",
-       {0x00, 0x00}},
-      // first ack block length.
-      {"Unable to read first ack block length.",
-       {0x88, 0x88}},
-      // num timestamps.
-      {"Underflow with first ack block length 34952 largest acked is 4660.",
-       {0x00}}
-  };
-
-  PacketFragments packet39 = {
-      // public flags (8 byte connection_id)
-      {"",
-       {0x2C}},
-      // connection_id
-      {"",
-       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
-      // packet number
-      {"",
        {0x12, 0x34, 0x56, 0x78}},
       // frame type (ack frame)
       // (one ack block, 2 byte largest observed, 2 byte block length)
@@ -3199,7 +2838,7 @@
        {0x00}}
   };
 
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
       // type (short header, 4 byte packet number)
       {"",
        {0x43}},
@@ -3258,13 +2897,10 @@
   PacketFragments& fragments =
       framer_.transport_version() == QUIC_VERSION_99
           ? packet99
-          : (framer_.transport_version() > QUIC_VERSION_46
-                 ? packet47
-                 : (framer_.transport_version() > QUIC_VERSION_43
-                        ? packet44
-                        : (framer_.transport_version() != QUIC_VERSION_35
-                               ? packet39
-                               : packet)));
+          : (framer_.transport_version() > QUIC_VERSION_44
+                 ? packet46
+                 : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+                                                                  : packet));
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
   EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
@@ -3557,47 +3193,6 @@
        { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }},
       // packet number
       {"",
-       { 0x78, 0x56, 0x34, 0x12 }},
-
-      // frame type (ack frame)
-      // (more than one ack block, 2 byte largest observed, 2 byte block length)
-      {"",
-       { 0x65 }},
-      // largest acked
-      {"Unable to read largest acked.",
-       { 0x34, 0x12 }},
-      // Zero delta time.
-      {"Unable to read ack delay time.",
-       { 0x00, 0x00 }},
-      // num ack blocks ranges.
-      {"Unable to read num of ack blocks.",
-       { 0x01 }},
-      // first ack block length.
-      {"Unable to read first ack block length.",
-       { 0x00, 0x00 }},
-      // gap to next block.
-      { "First block length is zero but ACK is not empty. "
-        "largest acked is 4660, num ack blocks is 1.",
-        { 0x01 }},
-      // ack block length.
-      { "First block length is zero but ACK is not empty. "
-        "largest acked is 4660, num ack blocks is 1.",
-        { 0xaf, 0x0e }},
-      // Number of timestamps.
-      { "First block length is zero but ACK is not empty. "
-        "largest acked is 4660, num ack blocks is 1.",
-        { 0x00 }},
-  };
-
-  PacketFragments packet39 = {
-      // public flags (8 byte connection_id)
-      {"",
-       { 0x2C }},
-      // connection_id
-      {"",
-       { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }},
-      // packet number
-      {"",
        { 0x12, 0x34, 0x56, 0x78 }},
 
       // frame type (ack frame)
@@ -3617,16 +3212,13 @@
       {"Unable to read first ack block length.",
        { 0x00, 0x00 }},
       // gap to next block.
-      { "First block length is zero but ACK is not empty. "
-        "largest acked is 4660, num ack blocks is 1.",
+      { "First block length is zero.",
         { 0x01 }},
       // ack block length.
-      { "First block length is zero but ACK is not empty. "
-        "largest acked is 4660, num ack blocks is 1.",
+      { "First block length is zero.",
         { 0x0e, 0xaf }},
       // Number of timestamps.
-      { "First block length is zero but ACK is not empty. "
-        "largest acked is 4660, num ack blocks is 1.",
+      { "First block length is zero.",
         { 0x00 }},
   };
 
@@ -3658,20 +3250,17 @@
       {"Unable to read first ack block length.",
        { 0x00, 0x00 }},
       // gap to next block.
-      { "First block length is zero but ACK is not empty. "
-        "largest acked is 4660, num ack blocks is 1.",
+      { "First block length is zero.",
         { 0x01 }},
       // ack block length.
-      { "First block length is zero but ACK is not empty. "
-        "largest acked is 4660, num ack blocks is 1.",
+      { "First block length is zero.",
         { 0x0e, 0xaf }},
       // Number of timestamps.
-      { "First block length is zero but ACK is not empty. "
-        "largest acked is 4660, num ack blocks is 1.",
+      { "First block length is zero.",
         { 0x00 }},
   };
 
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
       // type (short header, 4 byte packet number)
       {"",
        { 0x43 }},
@@ -3699,27 +3288,21 @@
       {"Unable to read first ack block length.",
        { 0x00, 0x00 }},
       // gap to next block.
-      { "First block length is zero but ACK is not empty. "
-        "largest acked is 4660, num ack blocks is 1.",
+      { "First block length is zero.",
         { 0x01 }},
       // ack block length.
-      { "First block length is zero but ACK is not empty. "
-        "largest acked is 4660, num ack blocks is 1.",
+      { "First block length is zero.",
         { 0x0e, 0xaf }},
       // Number of timestamps.
-      { "First block length is zero but ACK is not empty. "
-        "largest acked is 4660, num ack blocks is 1.",
+      { "First block length is zero.",
         { 0x00 }},
   };
 
   // clang-format on
   PacketFragments& fragments =
-      framer_.transport_version() > QUIC_VERSION_46
-          ? packet47
-          : (framer_.transport_version() > QUIC_VERSION_43
-                 ? packet44
-                 : (framer_.transport_version() != QUIC_VERSION_35 ? packet39
-                                                                   : packet));
+      framer_.transport_version() > QUIC_VERSION_44
+          ? packet46
+          : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet);
 
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
@@ -3749,34 +3332,6 @@
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"",
-       {0x78, 0x56, 0x34, 0x12}},
-      // frame type (ack frame)
-      // (one ack block, 4 byte largest observed, 2 byte block length)
-      {"",
-       {0x49}},
-      // largest acked
-      {"Unable to read largest acked.",
-       {0x78, 0x56, 0x34, 0x12}},
-      // Zero delta time.
-      {"Unable to read ack delay time.",
-       {0x00, 0x00}},
-      // first ack block length.
-      {"Unable to read first ack block length.",
-       {0x34, 0x12}},
-      // num timestamps.
-      {"Unable to read num received packets.",
-       {0x00}}
-  };
-
-  PacketFragments packet39 = {
-      // public flags (8 byte connection_id)
-      {"",
-       {0x2C}},
-      // connection_id
-      {"",
-       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
-      // packet number
-      {"",
        {0x12, 0x34, 0x56, 0x78}},
       // frame type (ack frame)
       // (one ack block, 4 byte largest observed, 2 byte block length)
@@ -3824,7 +3379,7 @@
        {0x00}}
   };
 
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
       // type (short header, 4 byte packet number)
       {"",
        {0x43}},
@@ -3883,13 +3438,10 @@
   PacketFragments& fragments =
       framer_.transport_version() == QUIC_VERSION_99
           ? packet99
-          : (framer_.transport_version() > QUIC_VERSION_46
-                 ? packet47
-                 : (framer_.transport_version() > QUIC_VERSION_43
-                        ? packet44
-                        : (framer_.transport_version() != QUIC_VERSION_35
-                               ? packet39
-                               : packet)));
+          : (framer_.transport_version() > QUIC_VERSION_44
+                 ? packet46
+                 : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+                                                                  : packet));
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
   EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
@@ -3922,74 +3474,6 @@
        { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }},
       // packet number
       {"",
-       { 0x78, 0x56, 0x34, 0x12 }},
-
-      // frame type (ack frame)
-      // (more than one ack block, 2 byte largest observed, 2 byte block length)
-      {"",
-       { 0x65 }},
-      // largest acked
-      {"Unable to read largest acked.",
-       { 0x34, 0x12 }},
-      // Zero delta time.
-      {"Unable to read ack delay time.",
-       { 0x00, 0x00 }},
-      // num ack blocks ranges.
-      {"Unable to read num of ack blocks.",
-       { 0x04 }},
-      // first ack block length.
-      {"Unable to read first ack block length.",
-       { 0x01, 0x00 }},
-      // gap to next block.
-      { "Unable to read gap to next ack block.",
-        { 0x01 }},
-      // ack block length.
-      { "Unable to ack block length.",
-        { 0xaf, 0x0e }},
-      // gap to next block.
-      { "Unable to read gap to next ack block.",
-        { 0xff }},
-      // ack block length.
-      { "Unable to ack block length.",
-        { 0x00, 0x00 }},
-      // gap to next block.
-      { "Unable to read gap to next ack block.",
-        { 0x91 }},
-      // ack block length.
-      { "Unable to ack block length.",
-        { 0xea, 0x01 }},
-      // gap to next block.
-      { "Unable to read gap to next ack block.",
-        { 0x05 }},
-      // ack block length.
-      { "Unable to ack block length.",
-        { 0x04, 0x00 }},
-      // Number of timestamps.
-      { "Unable to read num received packets.",
-        { 0x02 }},
-      // Delta from largest observed.
-      { "Unable to read sequence delta in received packets.",
-        { 0x01 }},
-      // Delta time.
-      { "Unable to read time delta in received packets.",
-        { 0x10, 0x32, 0x54, 0x76 }},
-      // Delta from largest observed.
-      { "Unable to read sequence delta in received packets.",
-        { 0x02 }},
-      // Delta time.
-      { "Unable to read incremental time delta in received packets.",
-        { 0x10, 0x32 }},
-  };
-
-  PacketFragments packet39 = {
-      // public flags (8 byte connection_id)
-      {"",
-       { 0x2C }},
-      // connection_id
-      {"",
-       { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }},
-      // packet number
-      {"",
        { 0x12, 0x34, 0x56, 0x78 }},
 
       // frame type (ack frame)
@@ -4117,7 +3601,7 @@
         { 0x32, 0x10 }},
   };
 
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
       // type (short header, 4 byte packet number)
       {"",
        { 0x43 }},
@@ -4245,13 +3729,10 @@
   PacketFragments& fragments =
       framer_.transport_version() == QUIC_VERSION_99
           ? packet99
-          : (framer_.transport_version() > QUIC_VERSION_46
-                 ? packet47
-                 : (framer_.transport_version() > QUIC_VERSION_43
-                        ? packet44
-                        : (framer_.transport_version() != QUIC_VERSION_35
-                               ? packet39
-                               : packet)));
+          : (framer_.transport_version() > QUIC_VERSION_44
+                 ? packet46
+                 : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+                                                                  : packet));
 
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
@@ -4279,6 +3760,200 @@
   CheckFramingBoundaries(fragments, QUIC_INVALID_ACK_DATA);
 }
 
+TEST_P(QuicFramerTest, AckFrameTimeStampDeltaTooHigh) {
+  // clang-format off
+  unsigned char packet[] = {
+      // public flags (8 byte connection_id)
+      0x28,
+      // connection_id
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // packet number
+      0x12, 0x34, 0x56, 0x78,
+
+      // frame type (ack frame)
+      // (no ack blocks, 1 byte largest observed, 1 byte block length)
+      0x40,
+      // largest acked
+      0x01,
+      // Zero delta time.
+      0x00, 0x00,
+      // first ack block length.
+      0x01,
+      // num timestamps.
+      0x01,
+      // Delta from largest observed.
+      0x01,
+      // Delta time.
+      0x10, 0x32, 0x54, 0x76,
+  };
+
+  unsigned char packet44[] = {
+      // type (short header, 4 byte packet number)
+      0x32,
+      // connection_id
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // packet number
+      0x12, 0x34, 0x56, 0x78,
+
+      // frame type (ack frame)
+      // (no ack blocks, 1 byte largest observed, 1 byte block length)
+      0x40,
+      // largest acked
+      0x01,
+      // Zero delta time.
+      0x00, 0x00,
+      // first ack block length.
+      0x01,
+      // num timestamps.
+      0x01,
+      // Delta from largest observed.
+      0x01,
+      // Delta time.
+      0x10, 0x32, 0x54, 0x76,
+  };
+
+  unsigned char packet46[] = {
+      // type (short header, 4 byte packet number)
+      0x43,
+      // connection_id
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // packet number
+      0x12, 0x34, 0x56, 0x78,
+
+      // frame type (ack frame)
+      // (no ack blocks, 1 byte largest observed, 1 byte block length)
+      0x40,
+      // largest acked
+      0x01,
+      // Zero delta time.
+      0x00, 0x00,
+      // first ack block length.
+      0x01,
+      // num timestamps.
+      0x01,
+      // Delta from largest observed.
+      0x01,
+      // Delta time.
+      0x10, 0x32, 0x54, 0x76,
+  };
+  // clang-format on
+  if (framer_.transport_version() == QUIC_VERSION_99) {
+    return;
+  }
+  QuicEncryptedPacket encrypted(
+      AsChars(framer_.transport_version() > QUIC_VERSION_44
+                  ? packet46
+                  : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+                                                                   : packet)),
+      QUIC_ARRAYSIZE(packet), false);
+  EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+  EXPECT_TRUE(QuicTextUtils::StartsWith(
+      framer_.detailed_error(), "delta_from_largest_observed too high"));
+}
+
+TEST_P(QuicFramerTest, AckFrameTimeStampSecondDeltaTooHigh) {
+  // clang-format off
+  unsigned char packet[] = {
+      // public flags (8 byte connection_id)
+      0x28,
+      // connection_id
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // packet number
+      0x12, 0x34, 0x56, 0x78,
+
+      // frame type (ack frame)
+      // (no ack blocks, 1 byte largest observed, 1 byte block length)
+      0x40,
+      // largest acked
+      0x03,
+      // Zero delta time.
+      0x00, 0x00,
+      // first ack block length.
+      0x03,
+      // num timestamps.
+      0x02,
+      // Delta from largest observed.
+      0x01,
+      // Delta time.
+      0x10, 0x32, 0x54, 0x76,
+      // Delta from largest observed.
+      0x03,
+      // Delta time.
+      0x10, 0x32,
+  };
+
+  unsigned char packet44[] = {
+      // type (short header, 4 byte packet number)
+      0x32,
+      // connection_id
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // packet number
+      0x12, 0x34, 0x56, 0x78,
+
+      // frame type (ack frame)
+      // (no ack blocks, 1 byte largest observed, 1 byte block length)
+      0x40,
+      // largest acked
+      0x03,
+      // Zero delta time.
+      0x00, 0x00,
+      // first ack block length.
+      0x03,
+      // num timestamps.
+      0x02,
+      // Delta from largest observed.
+      0x01,
+      // Delta time.
+      0x10, 0x32, 0x54, 0x76,
+      // Delta from largest observed.
+      0x03,
+      // Delta time.
+      0x10, 0x32,
+  };
+
+  unsigned char packet46[] = {
+      // type (short header, 4 byte packet number)
+      0x43,
+      // connection_id
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // packet number
+      0x12, 0x34, 0x56, 0x78,
+
+      // frame type (ack frame)
+      // (no ack blocks, 1 byte largest observed, 1 byte block length)
+      0x40,
+      // largest acked
+      0x03,
+      // Zero delta time.
+      0x00, 0x00,
+      // first ack block length.
+      0x03,
+      // num timestamps.
+      0x02,
+      // Delta from largest observed.
+      0x01,
+      // Delta time.
+      0x10, 0x32, 0x54, 0x76,
+      // Delta from largest observed.
+      0x03,
+      // Delta time.
+      0x10, 0x32,
+  };
+  // clang-format on
+  if (framer_.transport_version() == QUIC_VERSION_99) {
+    return;
+  }
+  QuicEncryptedPacket encrypted(
+      AsChars(framer_.transport_version() > QUIC_VERSION_44
+                  ? packet46
+                  : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+                                                                   : packet)),
+      QUIC_ARRAYSIZE(packet), false);
+  EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+  EXPECT_TRUE(QuicTextUtils::StartsWith(
+      framer_.detailed_error(), "delta_from_largest_observed too high"));
+}
+
 TEST_P(QuicFramerTest, NewStopWaitingFrame) {
   if (version_.transport_version == QUIC_VERSION_99) {
     return;
@@ -4293,24 +3968,6 @@
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"",
-       {0x78, 0x56, 0x34, 0x12}},
-      // frame type (stop waiting frame)
-      {"",
-       {0x06}},
-      // least packet number awaiting an ack, delta from packet number.
-      {"Unable to read least unacked delta.",
-        {0x08, 0x00, 0x00, 0x00}}
-  };
-
-  PacketFragments packet39 = {
-      // public flags (8 byte connection_id)
-      {"",
-       {0x2C}},
-      // connection_id
-      {"",
-       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
-      // packet number
-      {"",
        {0x12, 0x34, 0x56, 0x78}},
       // frame type (stop waiting frame)
       {"",
@@ -4338,7 +3995,7 @@
         {0x00, 0x00, 0x00, 0x08}}
   };
 
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
       // type (short header, 4 byte packet number)
       {"",
        {0x43}},
@@ -4358,12 +4015,10 @@
   // clang-format on
 
   PacketFragments& fragments =
-      framer_.transport_version() > QUIC_VERSION_46
-          ? packet47
-          : (framer_.transport_version() > QUIC_VERSION_43
-                 ? packet44
-                 : (framer_.transport_version() != QUIC_VERSION_35 ? packet39
-                                                                   : packet));
+      framer_.transport_version() > QUIC_VERSION_44
+          ? packet46
+          : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet);
+
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
   EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
@@ -4393,20 +4048,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-    // frame type (stop waiting frame)
-    0x06,
-    // least packet number awaiting an ack, delta from packet number.
-    0xA8, 0x9A, 0x78, 0x56,
-    0x34, 0x13,
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id)
-    0x2C,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
     // frame type (stop waiting frame)
     0x06,
@@ -4428,7 +4069,7 @@
     0x57, 0x78, 0x9A, 0xA8,
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short header, 4 byte packet number)
     0x43,
     // connection_id
@@ -4443,13 +4084,10 @@
   // clang-format on
 
   QuicEncryptedPacket encrypted(
-      AsChars(framer_.transport_version() > QUIC_VERSION_46
-                  ? packet47
-                  : (framer_.transport_version() > QUIC_VERSION_43
-                         ? packet44
-                         : (framer_.transport_version() == QUIC_VERSION_35
-                                ? packet
-                                : packet39))),
+      AsChars(framer_.transport_version() > QUIC_VERSION_44
+                  ? packet46
+                  : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+                                                                   : packet)),
       framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44)
                                                     : QUIC_ARRAYSIZE(packet),
       false);
@@ -4469,31 +4107,6 @@
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"",
-       {0x78, 0x56, 0x34, 0x12}},
-      // frame type (rst stream frame)
-      {"",
-       {0x01}},
-      // stream id
-      {"Unable to read stream_id.",
-       {0x04, 0x03, 0x02, 0x01}},
-      // sent byte offset
-      {"Unable to read rst stream sent byte offset.",
-       {0x54, 0x76, 0x10, 0x32,
-        0xDC, 0xFE, 0x98, 0x3A}},
-      // error code
-      {"Unable to read rst stream error code.",
-       {0x01, 0x00, 0x00, 0x00}}
-  };
-
-  PacketFragments packet39 = {
-      // public flags (8 byte connection_id)
-      {"",
-       {0x28}},
-      // connection_id
-      {"",
-       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
-      // packet number
-      {"",
        {0x12, 0x34, 0x56, 0x78}},
       // frame type (rst stream frame)
       {"",
@@ -4535,7 +4148,7 @@
        {0x00, 0x00, 0x00, 0x01}}
   };
 
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
       // type (short header, 4 byte packet number)
       {"",
        {0x43}},
@@ -4588,13 +4201,10 @@
   PacketFragments& fragments =
       framer_.transport_version() == QUIC_VERSION_99
           ? packet99
-          : (framer_.transport_version() > QUIC_VERSION_46
-                 ? packet47
-                 : (framer_.transport_version() > QUIC_VERSION_43
-                        ? packet44
-                        : (framer_.transport_version() != QUIC_VERSION_35
-                               ? packet39
-                               : packet)));
+          : (framer_.transport_version() > QUIC_VERSION_44
+                 ? packet46
+                 : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+                                                                  : packet));
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
   EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
@@ -4622,34 +4232,6 @@
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"",
-       {0x78, 0x56, 0x34, 0x12}},
-      // frame type (connection close frame)
-      {"",
-       {0x02}},
-      // error code
-      {"Unable to read connection close error code.",
-       {0x11, 0x00, 0x00, 0x00}},
-      {"Unable to read connection close error details.",
-       {
-         // error details length
-         0x0d, 0x00,
-         // error details
-         'b',  'e',  'c',  'a',
-         'u',  's',  'e',  ' ',
-         'I',  ' ',  'c',  'a',
-         'n'}
-      }
-  };
-
-  PacketFragments packet39 = {
-      // public flags (8 byte connection_id)
-      {"",
-       {0x28}},
-      // connection_id
-      {"",
-       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
-      // packet number
-      {"",
        {0x12, 0x34, 0x56, 0x78}},
       // frame type (connection close frame)
       {"",
@@ -4697,7 +4279,7 @@
       }
   };
 
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
       // type (short header, 4 byte packet number)
       {"",
        {0x43}},
@@ -4759,13 +4341,10 @@
   PacketFragments& fragments =
       framer_.transport_version() == QUIC_VERSION_99
           ? packet99
-          : (framer_.transport_version() > QUIC_VERSION_46
-                 ? packet47
-                 : (framer_.transport_version() > QUIC_VERSION_43
-                        ? packet44
-                        : (framer_.transport_version() != QUIC_VERSION_35
-                               ? packet39
-                               : packet)));
+          : (framer_.transport_version() > QUIC_VERSION_44
+                 ? packet46
+                 : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+                                                                  : packet));
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
   EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
@@ -4860,37 +4439,6 @@
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"",
-       {0x78, 0x56, 0x34, 0x12}},
-      // frame type (go away frame)
-      {"",
-       {0x03}},
-      // error code
-      {"Unable to read go away error code.",
-       {0x09, 0x00, 0x00, 0x00}},
-      // stream id
-      {"Unable to read last good stream id.",
-       {0x04, 0x03, 0x02, 0x01}},
-      {"Unable to read goaway reason.",
-       {
-         // error details length
-         0x0d, 0x00,
-         // error details
-         'b',  'e',  'c',  'a',
-         'u',  's',  'e',  ' ',
-         'I',  ' ',  'c',  'a',
-         'n'}
-      }
-  };
-
-  PacketFragments packet39 = {
-      // public flags (8 byte connection_id)
-      {"",
-       {0x28}},
-      // connection_id
-      {"",
-       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
-      // packet number
-      {"",
        {0x12, 0x34, 0x56, 0x78}},
       // frame type (go away frame)
       {"",
@@ -4946,7 +4494,7 @@
       }
   };
 
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
       // type (short header, 4 byte packet number)
       {"",
        {0x43}},
@@ -4980,12 +4528,9 @@
   // clang-format on
 
   PacketFragments& fragments =
-      framer_.transport_version() > QUIC_VERSION_46
-          ? packet47
-          : (framer_.transport_version() > QUIC_VERSION_43
-                 ? packet44
-                 : (framer_.transport_version() != QUIC_VERSION_35 ? packet39
-                                                                   : packet));
+      framer_.transport_version() > QUIC_VERSION_44
+          ? packet46
+          : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet);
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
   EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
@@ -5019,28 +4564,6 @@
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"",
-       {0x78, 0x56, 0x34, 0x12}},
-      // frame type (window update frame)
-      {"",
-       {0x04}},
-      // stream id
-      {"Unable to read stream_id.",
-       {0x04, 0x03, 0x02, 0x01}},
-      // byte offset
-      {"Unable to read window byte_offset.",
-       {0x54, 0x76, 0x10, 0x32,
-        0xDC, 0xFE, 0x98, 0x3A}},
-  };
-
-  PacketFragments packet39 = {
-      // public flags (8 byte connection_id)
-      {"",
-       {0x28}},
-      // connection_id
-      {"",
-       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
-      // packet number
-      {"",
        {0x12, 0x34, 0x56, 0x78}},
       // frame type (window update frame)
       {"",
@@ -5076,7 +4599,7 @@
         0x32, 0x10, 0x76, 0x54}},
   };
 
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
       // type (short header, 4 byte packet number)
       {"",
        {0x43}},
@@ -5101,12 +4624,9 @@
   // clang-format on
 
   PacketFragments& fragments =
-      framer_.transport_version() > QUIC_VERSION_46
-          ? packet47
-          : (framer_.transport_version() > QUIC_VERSION_43
-                 ? packet44
-                 : (framer_.transport_version() != QUIC_VERSION_35 ? packet39
-                                                                   : packet));
+      framer_.transport_version() > QUIC_VERSION_44
+          ? packet46
+          : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet);
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
   EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
@@ -5159,7 +4679,8 @@
       *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
       PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
 
-  EXPECT_EQ(0u, visitor_.window_update_frame_.stream_id);
+  EXPECT_EQ(QuicUtils::GetInvalidStreamId(framer_.transport_version()),
+            visitor_.window_update_frame_.stream_id);
   EXPECT_EQ(kStreamOffset, visitor_.window_update_frame_.byte_offset);
 
   CheckFramingBoundaries(packet99, QUIC_INVALID_MAX_DATA_FRAME_DATA);
@@ -5221,24 +4742,6 @@
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"",
-       {0x78, 0x56, 0x34, 0x12}},
-      // frame type (blocked frame)
-      {"",
-       {0x05}},
-      // stream id
-      {"Unable to read stream_id.",
-       {0x04, 0x03, 0x02, 0x01}},
-  };
-
-  PacketFragments packet39 = {
-      // public flags (8 byte connection_id)
-      {"",
-       {0x28}},
-      // connection_id
-      {"",
-       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
-      // packet number
-      {"",
        {0x12, 0x34, 0x56, 0x78}},
       // frame type (blocked frame)
       {"",
@@ -5266,7 +4769,7 @@
        {0x01, 0x02, 0x03, 0x04}},
   };
 
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
       // type (short header, 4 byte packet number)
       {"",
        {0x43}},
@@ -5309,13 +4812,10 @@
   PacketFragments& fragments =
       framer_.transport_version() == QUIC_VERSION_99
           ? packet99
-          : (framer_.transport_version() > QUIC_VERSION_46
-                 ? packet47
-                 : (framer_.transport_version() > QUIC_VERSION_43
-                        ? packet44
-                        : (framer_.transport_version() != QUIC_VERSION_35
-                               ? packet39
-                               : packet)));
+          : (framer_.transport_version() > QUIC_VERSION_44
+                 ? packet46
+                 : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+                                                                  : packet));
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
   EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
@@ -5348,18 +4848,6 @@
      // connection_id
      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
      // packet number
-     0x78, 0x56, 0x34, 0x12,
-
-     // frame type (ping frame)
-     0x07,
-    };
-
-  unsigned char packet39[] = {
-     // public flags (8 byte connection_id)
-     0x28,
-     // connection_id
-     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-     // packet number
      0x12, 0x34, 0x56, 0x78,
 
      // frame type (ping frame)
@@ -5378,7 +4866,7 @@
      0x07,
     };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
      // type (short header, 4 byte packet number)
      0x43,
      // connection_id
@@ -5406,17 +4894,15 @@
   QuicEncryptedPacket encrypted(
       AsChars(framer_.transport_version() == QUIC_VERSION_99
                   ? packet99
-                  : (framer_.transport_version() > QUIC_VERSION_46
-                         ? packet47
+                  : (framer_.transport_version() > QUIC_VERSION_44
+                         ? packet46
                          : framer_.transport_version() > QUIC_VERSION_43
                                ? packet44
-                               : (framer_.transport_version() == QUIC_VERSION_35
-                                      ? packet
-                                      : packet39))),
+                               : packet)),
       framer_.transport_version() == QUIC_VERSION_99
           ? QUIC_ARRAYSIZE(packet99)
-          : (framer_.transport_version() > QUIC_VERSION_46
-                 ? QUIC_ARRAYSIZE(packet47)
+          : (framer_.transport_version() > QUIC_VERSION_44
+                 ? QUIC_ARRAYSIZE(packet46)
                  : framer_.transport_version() > QUIC_VERSION_43
                        ? QUIC_ARRAYSIZE(packet44)
                        : QUIC_ARRAYSIZE(packet)),
@@ -5466,7 +4952,7 @@
          {'m', 'e', 's', 's', 'a', 'g', 'e', '2'}},
    };
 
-  PacketFragments packet47 = {
+  PacketFragments packet46 = {
        // type (short header, 4 byte packet number)
        {"",
         {0x43}},
@@ -5495,7 +4981,7 @@
   // clang-format on
 
   std::unique_ptr<QuicEncryptedPacket> encrypted(AssemblePacketFromFragments(
-      framer_.transport_version() > QUIC_VERSION_46 ? packet47 : packet45));
+      framer_.transport_version() > QUIC_VERSION_44 ? packet46 : packet45));
   EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
 
   EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
@@ -5505,11 +4991,11 @@
       PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
 
   ASSERT_EQ(2u, visitor_.message_frames_.size());
-  EXPECT_EQ(7u, visitor_.message_frames_[0]->message_data.length());
-  EXPECT_EQ(8u, visitor_.message_frames_[1]->message_data.length());
+  EXPECT_EQ(7u, visitor_.message_frames_[0]->message_length);
+  EXPECT_EQ(8u, visitor_.message_frames_[1]->message_length);
 
   CheckFramingBoundaries(
-      framer_.transport_version() > QUIC_VERSION_46 ? packet47 : packet45,
+      framer_.transport_version() > QUIC_VERSION_44 ? packet46 : packet45,
       QUIC_INVALID_MESSAGE_DATA);
 }
 
@@ -5744,7 +5230,7 @@
                        QuicMakeUnique<NullDecrypter>(Perspective::IS_CLIENT));
   decrypter_ = new test::TestDecrypter();
   framer_.SetAlternativeDecrypter(
-      ENCRYPTION_INITIAL, std::unique_ptr<QuicDecrypter>(decrypter_), false);
+      ENCRYPTION_ZERO_RTT, std::unique_ptr<QuicDecrypter>(decrypter_), false);
   // This packet cannot be decrypted because diversification nonce is missing.
   QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false);
   EXPECT_TRUE(framer_.ProcessPacket(encrypted));
@@ -5774,7 +5260,7 @@
                        QuicMakeUnique<NullDecrypter>(Perspective::IS_CLIENT));
   decrypter_ = new test::TestDecrypter();
   framer_.SetAlternativeDecrypter(
-      ENCRYPTION_INITIAL, std::unique_ptr<QuicDecrypter>(decrypter_), false);
+      ENCRYPTION_ZERO_RTT, std::unique_ptr<QuicDecrypter>(decrypter_), false);
   // This packet cannot be decrypted because diversification nonce is missing.
   QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false);
   EXPECT_FALSE(framer_.ProcessPacket(encrypted));
@@ -5892,19 +5378,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // frame type (padding frame)
-    0x00,
-    0x00, 0x00, 0x00, 0x00
-  };
-
-  unsigned char packet39[kMaxPacketSize] = {
-    // public flags (8 byte connection_id)
-    0x28,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // frame type (padding frame)
@@ -5925,7 +5398,7 @@
     0x00, 0x00, 0x00, 0x00
   };
 
-  unsigned char packet47[kMaxPacketSize] = {
+  unsigned char packet46[kMaxPacketSize] = {
     // type (short header, 4 byte packet number)
     0x43,
     // connection_id
@@ -5955,18 +5428,17 @@
   unsigned char* p = packet;
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   uint64_t header_size = GetPacketHeaderSize(
       framer_.transport_version(), PACKET_8BYTE_CONNECTION_ID,
       PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion,
-      !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER);
+      !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER,
+      VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0);
   memset(p + header_size + 1, 0x00, kMaxPacketSize - header_size - 1);
 
   std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
@@ -5979,9 +5451,6 @@
 }
 
 TEST_P(QuicFramerTest, BuildStreamFramePacketWithNewPaddingFrame) {
-  if (framer_.transport_version() == QUIC_VERSION_35) {
-    return;
-  }
   QuicPacketHeader header;
   header.destination_connection_id = FramerTestConnectionId();
   header.reset_flag = false;
@@ -6000,33 +5469,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // paddings
-    0x00, 0x00,
-    // frame type (stream frame with fin)
-    0xFF,
-    // stream id
-    0x04, 0x03, 0x02, 0x01,
-    // offset
-    0x54, 0x76, 0x10, 0x32,
-    0xDC, 0xFE, 0x98, 0x3A,
-    // data length
-    0x0c, 0x00,
-    // data
-    'h',  'e',  'l',  'l',
-    'o',  ' ',  'w',  'o',
-    'r',  'l',  'd',  '!',
-    // paddings
-    0x00, 0x00,
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id)
-    0x28,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // paddings
@@ -6075,7 +5517,7 @@
     0x00, 0x00,
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short header, 4 byte packet number)
     0x43,
     // connection_id
@@ -6138,14 +5580,12 @@
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
     p_size = QUIC_ARRAYSIZE(packet99);
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
-    p_size = QUIC_ARRAYSIZE(packet47);
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
+    p_size = QUIC_ARRAYSIZE(packet46);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
   QuicEncryptedPacket encrypted(AsChars(p), p_size, false);
 
@@ -6170,19 +5610,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // frame type (padding frame)
-    0x00,
-    0x00, 0x00, 0x00, 0x00
-  };
-
-  unsigned char packet39[kMaxPacketSize] = {
-    // public flags (8 byte connection_id and 4 byte packet number)
-    0x28,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // frame type (padding frame)
@@ -6203,7 +5630,7 @@
     0x00, 0x00, 0x00, 0x00
   };
 
-  unsigned char packet47[kMaxPacketSize] = {
+  unsigned char packet46[kMaxPacketSize] = {
     // type (short header, 4 byte packet number)
     0x43,
     // connection_id
@@ -6233,18 +5660,17 @@
   unsigned char* p = packet;
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   uint64_t header_size = GetPacketHeaderSize(
       framer_.transport_version(), PACKET_8BYTE_CONNECTION_ID,
       PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion,
-      !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER);
+      !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER,
+      VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0);
   memset(p + header_size + 1, 0x00, kMaxPacketSize - header_size - 1);
 
   std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
@@ -6273,19 +5699,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56,
-
-    // frame type (padding frame)
-    0x00,
-    0x00, 0x00, 0x00, 0x00
-  };
-
-  unsigned char packet39[kMaxPacketSize] = {
-    // public flags (8 byte connection_id and 2 byte packet number)
-    0x18,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x56, 0x78,
 
     // frame type (padding frame)
@@ -6306,7 +5719,7 @@
     0x00, 0x00, 0x00, 0x00
   };
 
-  unsigned char packet47[kMaxPacketSize] = {
+  unsigned char packet46[kMaxPacketSize] = {
     // type (short header, 2 byte packet number)
     0x41,
     // connection_id
@@ -6336,18 +5749,17 @@
   unsigned char* p = packet;
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   uint64_t header_size = GetPacketHeaderSize(
       framer_.transport_version(), PACKET_8BYTE_CONNECTION_ID,
       PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion,
-      !kIncludeDiversificationNonce, PACKET_2BYTE_PACKET_NUMBER);
+      !kIncludeDiversificationNonce, PACKET_2BYTE_PACKET_NUMBER,
+      VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0);
   memset(p + header_size + 1, 0x00, kMaxPacketSize - header_size - 1);
 
   std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
@@ -6396,7 +5808,7 @@
     0x00, 0x00, 0x00, 0x00
   };
 
-  unsigned char packet47[kMaxPacketSize] = {
+  unsigned char packet46[kMaxPacketSize] = {
     // type (short header, 1 byte packet number)
     0x40,
     // connection_id
@@ -6426,8 +5838,8 @@
   unsigned char* p = packet;
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
   }
@@ -6435,7 +5847,8 @@
   uint64_t header_size = GetPacketHeaderSize(
       framer_.transport_version(), PACKET_8BYTE_CONNECTION_ID,
       PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion,
-      !kIncludeDiversificationNonce, PACKET_1BYTE_PACKET_NUMBER);
+      !kIncludeDiversificationNonce, PACKET_1BYTE_PACKET_NUMBER,
+      VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0);
   memset(p + header_size + 1, 0x00, kMaxPacketSize - header_size - 1);
 
   std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
@@ -6453,6 +5866,9 @@
   header.reset_flag = false;
   header.version_flag = false;
   header.packet_number = kPacketNumber;
+  if (QuicVersionHasLongHeaderLengths(framer_.transport_version())) {
+    header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+  }
 
   QuicStreamFrame stream_frame(kStreamId, true, kStreamOffset,
                                QuicStringPiece("hello world!"));
@@ -6466,27 +5882,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // frame type (stream frame with fin and no length)
-    0xDF,
-    // stream id
-    0x04, 0x03, 0x02, 0x01,
-    // offset
-    0x54, 0x76, 0x10, 0x32,
-    0xDC, 0xFE, 0x98, 0x3A,
-    // data
-    'h',  'e',  'l',  'l',
-    'o',  ' ',  'w',  'o',
-    'r',  'l',  'd',  '!',
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id)
-    0x28,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // frame type (stream frame with fin and no length)
@@ -6523,7 +5918,7 @@
     'r',  'l',  'd',  '!',
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short header, 4 byte packet number)
     0x43,
     // connection_id
@@ -6574,14 +5969,12 @@
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
     p_size = QUIC_ARRAYSIZE(packet99);
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
-    p_size = QUIC_ARRAYSIZE(packet47);
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
+    p_size = QUIC_ARRAYSIZE(packet46);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
   test::CompareCharArraysWithHexError("constructed packet", data->data(),
                                       data->length(), AsChars(p), p_size);
@@ -6596,6 +5989,9 @@
     header.long_packet_type = ZERO_RTT_PROTECTED;
   }
   header.packet_number = kPacketNumber;
+  if (QuicVersionHasLongHeaderLengths(framer_.transport_version())) {
+    header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+  }
 
   QuicStreamFrame stream_frame(kStreamId, true, kStreamOffset,
                                QuicStringPiece("hello world!"));
@@ -6610,26 +6006,6 @@
       // version tag
       QUIC_VERSION_BYTES,
       // packet number
-      0x78, 0x56, 0x34, 0x12,
-
-      // frame type (stream frame with fin and no length)
-      0xDF,
-      // stream id
-      0x04, 0x03, 0x02, 0x01,
-      // offset
-      0x54, 0x76, 0x10, 0x32, 0xDC, 0xFE, 0x98, 0x3A,
-      // data
-      'h',  'e',  'l',  'l',  'o',  ' ',  'w',  'o',  'r', 'l', 'd', '!',
-  };
-
-  unsigned char packet39[] = {
-      // public flags (version, 8 byte connection_id)
-      0x2D,
-      // connection_id
-      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-      // version tag
-      QUIC_VERSION_BYTES,
-      // packet number
       0x12, 0x34, 0x56, 0x78,
 
       // frame type (stream frame with fin and no length)
@@ -6664,7 +6040,7 @@
       'h',  'e',  'l',  'l',  'o',  ' ',  'w',  'o',  'r', 'l', 'd', '!',
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
       // type (long header with packet type ZERO_RTT_PROTECTED)
       0xD3,
       // version tag
@@ -6695,6 +6071,8 @@
       0x50,
       // connection_id
       0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // length
+      0x40, 0x1D,
       // packet number
       0x12, 0x34, 0x56, 0x78,
 
@@ -6718,14 +6096,12 @@
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
     p_size = QUIC_ARRAYSIZE(packet99);
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
-    p_size = QUIC_ARRAYSIZE(packet47);
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
+    p_size = QUIC_ARRAYSIZE(packet46);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
   test::CompareCharArraysWithHexError("constructed packet", data->data(),
                                       data->length(), AsChars(p), p_size);
@@ -6896,27 +6272,6 @@
       // connection_id
       0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
       // packet number
-      0x78, 0x56, 0x34, 0x12,
-
-      // frame type (ack frame)
-      // (no ack blocks, 2 byte largest observed, 2 byte block length)
-      0x45,
-      // largest acked
-      0x34, 0x12,
-      // Zero delta time.
-      0x00, 0x00,
-      // first ack block length.
-      0x34, 0x12,
-      // num timestamps.
-      0x00,
-  };
-
-  unsigned char packet39[] = {
-      // public flags (8 byte connection_id)
-      0x28,
-      // connection_id
-      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-      // packet number
       0x12, 0x34, 0x56, 0x78,
 
       // frame type (ack frame)
@@ -6953,7 +6308,7 @@
       0x00,
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
       // type (short header, 4 byte packet number)
       0x43,
       // connection_id
@@ -6999,14 +6354,12 @@
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
     p_size = QUIC_ARRAYSIZE(packet99);
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
-    p_size = QUIC_ARRAYSIZE(packet47);
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
+    p_size = QUIC_ARRAYSIZE(packet46);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
@@ -7034,27 +6387,6 @@
       // connection_id
       0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
       // packet number
-      0x78, 0x56, 0x34, 0x12,
-
-      // frame type (ack frame)
-      // (no ack blocks, 4 byte largest observed, 4 byte block length)
-      0x4A,
-      // largest acked
-      0x78, 0x56, 0x34, 0x12,
-      // Zero delta time.
-      0x00, 0x00,
-      // first ack block length.
-      0x78, 0x56, 0x34, 0x12,
-      // num timestamps.
-      0x00,
-  };
-
-  unsigned char packet39[] = {
-      // public flags (8 byte connection_id)
-      0x28,
-      // connection_id
-      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-      // packet number
       0x12, 0x34, 0x56, 0x78,
 
       // frame type (ack frame)
@@ -7091,7 +6423,7 @@
       0x00,
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
       // type (short header, 4 byte packet number)
       0x43,
       // connection_id
@@ -7138,14 +6470,12 @@
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
     p_size = QUIC_ARRAYSIZE(packet99);
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
-    p_size = QUIC_ARRAYSIZE(packet47);
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
+    p_size = QUIC_ARRAYSIZE(packet46);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
@@ -7178,45 +6508,6 @@
       // connection_id
       0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
       // packet number
-      0x78, 0x56, 0x34, 0x12,
-
-      // frame type (ack frame)
-      // (has ack blocks, 2 byte largest observed, 2 byte block length)
-      0x65,
-      // largest acked
-      0x34, 0x12,
-      // Zero delta time.
-      0x00, 0x00,
-      // num ack blocks ranges.
-      0x04,
-      // first ack block length.
-      0x01, 0x00,
-      // gap to next block.
-      0x01,
-      // ack block length.
-      0xaf, 0x0e,
-      // gap to next block.
-      0xff,
-      // ack block length.
-      0x00, 0x00,
-      // gap to next block.
-      0x91,
-      // ack block length.
-      0xea, 0x01,
-      // gap to next block.
-      0x05,
-      // ack block length.
-      0x04, 0x00,
-      // num timestamps.
-      0x00,
-  };
-
-  unsigned char packet39[] = {
-      // public flags (8 byte connection_id)
-      0x28,
-      // connection_id
-      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-      // packet number
       0x12, 0x34, 0x56, 0x78,
 
       // frame type (ack frame)
@@ -7289,7 +6580,7 @@
       0x00,
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
       // type (short header, 4 byte packet number)
       0x43,
       // connection_id
@@ -7368,14 +6659,12 @@
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
     p_size = QUIC_ARRAYSIZE(packet99);
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
-    p_size = QUIC_ARRAYSIZE(packet47);
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
+    p_size = QUIC_ARRAYSIZE(packet46);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
@@ -7411,99 +6700,6 @@
       // connection_id
       0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
       // packet number
-      0x78, 0x56, 0x34, 0x12,
-      // frame type (ack frame)
-      // (has ack blocks, 2 byte largest observed, 2 byte block length)
-      0x65,
-      // largest acked
-      0x34, 0x12,
-      // Zero delta time.
-      0x00, 0x00,
-      // num ack blocks ranges.
-      0xff,
-      // first ack block length.
-      0xdd, 0x0f,
-      // 255 = 4 * 63 + 3
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00,
-      // num timestamps.
-      0x00,
-  };
-
-  unsigned char packet39[] = {
-      // public flags (8 byte connection_id)
-      0x28,
-      // connection_id
-      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-      // packet number
       0x12, 0x34, 0x56, 0x78,
       // frame type (ack frame)
       // (has ack blocks, 2 byte largest observed, 2 byte block length)
@@ -7684,7 +6880,7 @@
       0x00,
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
       // type (short header, 4 byte packet number)
       0x43,
       // connection_id
@@ -7867,14 +7063,12 @@
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
     p_size = QUIC_ARRAYSIZE(packet99);
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
-    p_size = QUIC_ARRAYSIZE(packet47);
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
+    p_size = QUIC_ARRAYSIZE(packet46);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
@@ -7906,20 +7100,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // frame type (stop waiting frame)
-    0x06,
-    // least packet number awaiting an ack, delta from packet number.
-    0x08, 0x00, 0x00, 0x00,
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id)
-    0x28,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // frame type (stop waiting frame)
@@ -7930,18 +7110,12 @@
 
   // clang-format on
 
-  unsigned char* p = packet;
-  if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
-  }
-
   std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
   ASSERT_TRUE(data != nullptr);
 
-  test::CompareCharArraysWithHexError(
-      "constructed packet", data->data(), data->length(), AsChars(p),
-      framer_.transport_version() != QUIC_VERSION_35 ? QUIC_ARRAYSIZE(packet39)
-                                                     : QUIC_ARRAYSIZE(packet));
+  test::CompareCharArraysWithHexError("constructed packet", data->data(),
+                                      data->length(), AsChars(packet),
+                                      QUIC_ARRAYSIZE(packet));
 }
 
 TEST_P(QuicFramerTest, BuildRstFramePacketQuic) {
@@ -7967,25 +7141,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // 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
-    0x08, 0x07, 0x06, 0x05,
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id)
-    0x28,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // frame type (rst stream frame)
@@ -8018,7 +7173,7 @@
     0x05, 0x06, 0x07, 0x08,
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short packet, 4 byte packet number)
     0x43,
     // connection_id
@@ -8066,14 +7221,12 @@
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
     p_size = QUIC_ARRAYSIZE(packet99);
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
-    p_size = QUIC_ARRAYSIZE(packet47);
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
+    p_size = QUIC_ARRAYSIZE(packet46);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
   QuicEncryptedPacket encrypted(AsChars(p), p_size, false);
 
@@ -8107,27 +7260,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // frame type (connection close frame)
-    0x02,
-    // error code
-    0x08, 0x07, 0x06, 0x05,
-    // error details length
-    0x0d, 0x00,
-    // error details
-    'b',  'e',  'c',  'a',
-    'u',  's',  'e',  ' ',
-    'I',  ' ',  'c',  'a',
-    'n',
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id)
-    0x28,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // frame type (connection close frame)
@@ -8164,7 +7296,7 @@
     'n',
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short header, 4 byte packet number)
     0x43,
     // connection_id
@@ -8214,14 +7346,12 @@
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
     p_size = QUIC_ARRAYSIZE(packet99);
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
-    p_size = QUIC_ARRAYSIZE(packet47);
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
+    p_size = QUIC_ARRAYSIZE(packet46);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
@@ -8255,55 +7385,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // frame type (connection close frame)
-    0x02,
-    // error code
-    0x08, 0x07, 0x06, 0x05,
-    // error details length
-    0x00, 0x01,
-    // error details (truncated to 256 bytes)
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id)
-    0x28,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // frame type (connection close frame)
@@ -8396,7 +7477,7 @@
     'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short header, 4 byte packet number)
     0x43,
     // connection_id
@@ -8502,14 +7583,12 @@
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
     p_size = QUIC_ARRAYSIZE(packet99);
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
-    p_size = QUIC_ARRAYSIZE(packet47);
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
+    p_size = QUIC_ARRAYSIZE(packet46);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
@@ -8669,29 +7748,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // frame type (go away frame)
-    0x03,
-    // error code
-    0x08, 0x07, 0x06, 0x05,
-    // stream id
-    0x04, 0x03, 0x02, 0x01,
-    // error details length
-    0x0d, 0x00,
-    // error details
-    'b',  'e',  'c',  'a',
-    'u',  's',  'e',  ' ',
-    'I',  ' ',  'c',  'a',
-    'n',
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id)
-    0x28,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // frame type (go away frame)
@@ -8732,7 +7788,7 @@
     'n',
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short header, 4 byte packet number)
     0x43,
     // connection_id
@@ -8759,14 +7815,12 @@
 
   unsigned char* p = packet;
   size_t p_size = QUIC_ARRAYSIZE(packet);
-  if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
-    p_size = QUIC_ARRAYSIZE(packet47);
+  if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
+    p_size = QUIC_ARRAYSIZE(packet46);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
@@ -8801,57 +7855,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // frame type (go away frame)
-    0x03,
-    // error code
-    0x08, 0x07, 0x06, 0x05,
-    // stream id
-    0x04, 0x03, 0x02, 0x01,
-    // error details length
-    0x00, 0x01,
-    // error details (truncated to 256 bytes)
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-    'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id)
-    0x28,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // frame type (go away frame)
@@ -8948,7 +7951,7 @@
     'A',  'A',  'A',  'A',  'A',  'A',  'A',  'A',
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short header, 4 byte packet number)
     0x43,
     // connection_id
@@ -9002,14 +8005,12 @@
 
   unsigned char* p = packet;
   size_t p_size = QUIC_ARRAYSIZE(packet);
-  if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
-    p_size = QUIC_ARRAYSIZE(packet47);
+  if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
+    p_size = QUIC_ARRAYSIZE(packet46);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
@@ -9039,23 +8040,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // frame type (window update frame)
-    0x04,
-    // stream id
-    0x04, 0x03, 0x02, 0x01,
-    // byte offset
-    0x88, 0x77, 0x66, 0x55,
-    0x44, 0x33, 0x22, 0x11,
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id)
-    0x28,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // frame type (window update frame)
@@ -9084,7 +8068,7 @@
     0x55, 0x66, 0x77, 0x88,
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short header, 4 byte packet number)
     0x43,
     // connection_id
@@ -9127,14 +8111,12 @@
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
     p_size = QUIC_ARRAYSIZE(packet99);
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
-    p_size = QUIC_ARRAYSIZE(packet47);
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
+    p_size = QUIC_ARRAYSIZE(packet46);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   test::CompareCharArraysWithHexError("constructed packet", data->data(),
@@ -9197,7 +8179,8 @@
   header.packet_number = kPacketNumber;
 
   QuicWindowUpdateFrame window_update_frame;
-  window_update_frame.stream_id = 0;
+  window_update_frame.stream_id =
+      QuicUtils::GetInvalidStreamId(framer_.transport_version());
   window_update_frame.byte_offset = 0x1122334455667788;
 
   QuicFrames frames = {QuicFrame(&window_update_frame)};
@@ -9236,12 +8219,11 @@
 
   QuicBlockedFrame blocked_frame;
   if (framer_.transport_version() == QUIC_VERSION_99) {
-    // For V99, the stream ID must be 0 for the frame
-    // to be a BLOCKED frame. if non-0, it will be a
+    // For V99, the stream ID must be <invalid> for the frame
+    // to be a BLOCKED frame. if it's valid, it will be a
     // STREAM_BLOCKED frame.
-    // TODO(fkastenholz): This should be converted to use
-    // QuicUtils::GetInvalidStreamId to get the correct invalid stream id value.
-    blocked_frame.stream_id = 0;
+    blocked_frame.stream_id =
+        QuicUtils::GetInvalidStreamId(framer_.transport_version());
   } else {
     blocked_frame.stream_id = kStreamId;
   }
@@ -9256,20 +8238,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // frame type (blocked frame)
-    0x05,
-    // stream id
-    0x04, 0x03, 0x02, 0x01,
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id)
-    0x28,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // frame type (blocked frame)
@@ -9292,7 +8260,7 @@
     0x01, 0x02, 0x03, 0x04,
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short packet, 4 byte packet number)
     0x43,
     // connection_id
@@ -9314,7 +8282,7 @@
     // packet number
     0x12, 0x34, 0x56, 0x78,
 
-    // frame type (IETF_BLOCKED frame) wahoo
+    // frame type (IETF_BLOCKED frame)
     0x14,
     // Offset
     kVarInt62EightBytes + 0x3a, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54
@@ -9329,14 +8297,12 @@
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
     p_size = QUIC_ARRAYSIZE(packet99);
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
-    p_size = QUIC_ARRAYSIZE(packet47);
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
+    p_size = QUIC_ARRAYSIZE(packet46);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   test::CompareCharArraysWithHexError("constructed packet", data->data(),
@@ -9359,18 +8325,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // frame type (ping frame)
-    0x07,
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id)
-    0x28,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // frame type (ping frame)
@@ -9389,7 +8343,7 @@
     0x07,
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short header, 4 byte packet number)
     0x43,
     // connection_id
@@ -9417,12 +8371,10 @@
   unsigned char* p = packet;
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
@@ -9443,9 +8395,13 @@
   header.reset_flag = false;
   header.version_flag = false;
   header.packet_number = kPacketNumber;
+  QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
 
-  QuicMessageFrame frame(1, "message");
-  QuicMessageFrame frame2(2, "message2");
+  QuicMessageFrame frame(1);
+  MakeSpan(&allocator_, "message", &storage).SaveMemSlicesAsMessageData(&frame);
+  QuicMessageFrame frame2(2);
+  MakeSpan(&allocator_, "message2", &storage)
+      .SaveMemSlicesAsMessageData(&frame2);
   QuicFrames frames = {QuicFrame(&frame), QuicFrame(&frame2)};
 
   // clang-format off
@@ -9469,7 +8425,7 @@
     'm', 'e', 's', 's', 'a', 'g', 'e', '2'
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short header, 4 byte packet number)
     0x43,
     // connection_id
@@ -9513,8 +8469,8 @@
   unsigned char* p = packet45;
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
   }
 
   std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
@@ -9541,21 +8497,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // frame type (ping frame)
-    0x07,
-    // frame type (padding frame)
-    0x00,
-    0x00, 0x00, 0x00, 0x00
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id)
-    0x28,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // frame type (ping frame)
@@ -9580,7 +8521,7 @@
     0x00, 0x00, 0x00, 0x00
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short header, 4 byte packet number)
     0x43,
     // connection_id
@@ -9616,70 +8557,21 @@
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
     packet_size = QUIC_ARRAYSIZE(packet99);
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
-    packet_size = QUIC_ARRAYSIZE(packet47);
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
+    packet_size = QUIC_ARRAYSIZE(packet46);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     packet_size = QUIC_ARRAYSIZE(packet44);
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
-    packet_size = QUIC_ARRAYSIZE(packet39);
   }
 
   std::unique_ptr<char[]> buffer(new char[kMaxPacketSize]);
 
-  size_t length =
-      framer_.BuildConnectivityProbingPacket(header, buffer.get(), packet_size);
+  size_t length = framer_.BuildConnectivityProbingPacket(
+      header, buffer.get(), packet_size, ENCRYPTION_NONE);
 
   EXPECT_NE(0u, length);
-  QuicPacket data(buffer.release(), length, true,
-                  header.destination_connection_id_length,
-                  header.source_connection_id_length, header.version_flag,
-                  header.nonce != nullptr, header.packet_number_length);
-
-  test::CompareCharArraysWithHexError("constructed packet", data.data(),
-                                      data.length(), AsChars(p), packet_size);
-}
-
-// Test that the IETF connectivity probing packet is serialized correctly as a
-// padded PING packet, v99 only.
-TEST_P(QuicFramerTest, BuildIetfConnectivityProbingPacket) {
-  if (framer_.transport_version() != QUIC_VERSION_99) {
-    return;
-  }
-  QuicPacketHeader header;
-  header.destination_connection_id = FramerTestConnectionId();
-  header.reset_flag = false;
-  header.version_flag = false;
-  header.packet_number = kPacketNumber;
-
-  unsigned char packet99[] = {// type (short header, 4 byte packet number)
-                              0x43,
-                              // connection_id
-                              0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-                              // packet number
-                              0x12, 0x34, 0x56, 0x78,
-
-                              // frame type (IETF_PING frame)
-                              0x01,
-                              // frame type (padding frame)
-                              0x00, 0x00, 0x00, 0x00, 0x00};
-  // clang-format on
-
-  unsigned char* p = packet99;
-  size_t packet_size = QUIC_ARRAYSIZE(packet99);
-
-  std::unique_ptr<char[]> buffer(new char[kMaxPacketSize]);
-
-  size_t length = framer_.BuildIetfConnectivityProbingPacket(
-      header, buffer.get(), packet_size);
-
-  EXPECT_NE(0u, length);
-  QuicPacket data(buffer.release(), length, true,
-                  header.destination_connection_id_length,
-                  header.source_connection_id_length, header.version_flag,
-                  header.nonce != nullptr, header.packet_number_length);
+  QuicPacket data(buffer.release(), length, true, header);
 
   test::CompareCharArraysWithHexError("constructed packet", data.data(),
                                       data.length(), AsChars(p), packet_size);
@@ -9722,17 +8614,15 @@
   MockRandom randomizer;
 
   size_t length = framer_.BuildPaddedPathChallengePacket(
-      header, buffer.get(), QUIC_ARRAYSIZE(packet), &payload, &randomizer);
+      header, buffer.get(), QUIC_ARRAYSIZE(packet), &payload, &randomizer,
+      ENCRYPTION_NONE);
   EXPECT_EQ(length, QUIC_ARRAYSIZE(packet));
 
   // Payload has the random bytes that were generated. Copy them into packet,
   // above, before checking that the generated packet is correct.
   EXPECT_EQ(kQuicPathFrameBufferSize, payload.size());
 
-  QuicPacket data(buffer.release(), length, true,
-                  header.destination_connection_id_length,
-                  header.source_connection_id_length, header.version_flag,
-                  header.nonce != nullptr, header.packet_number_length);
+  QuicPacket data(buffer.release(), length, true, header);
 
   test::CompareCharArraysWithHexError("constructed packet", data.data(),
                                       data.length(), AsChars(packet),
@@ -9777,12 +8667,9 @@
   payloads.push_back(payload0);
   size_t length = framer_.BuildPathResponsePacket(
       header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads,
-      /*is_padded=*/false);
+      /*is_padded=*/false, ENCRYPTION_NONE);
   EXPECT_EQ(length, QUIC_ARRAYSIZE(packet));
-  QuicPacket data(buffer.release(), length, true,
-                  header.destination_connection_id_length,
-                  header.source_connection_id_length, header.version_flag,
-                  header.nonce != nullptr, header.packet_number_length);
+  QuicPacket data(buffer.release(), length, true, header);
 
   test::CompareCharArraysWithHexError("constructed packet", data.data(),
                                       data.length(), AsChars(packet),
@@ -9825,12 +8712,9 @@
   payloads.push_back(payload0);
   size_t length = framer_.BuildPathResponsePacket(
       header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads,
-      /*is_padded=*/true);
+      /*is_padded=*/true, ENCRYPTION_NONE);
   EXPECT_EQ(length, QUIC_ARRAYSIZE(packet));
-  QuicPacket data(buffer.release(), length, true,
-                  header.destination_connection_id_length,
-                  header.source_connection_id_length, header.version_flag,
-                  header.nonce != nullptr, header.packet_number_length);
+  QuicPacket data(buffer.release(), length, true, header);
 
   test::CompareCharArraysWithHexError("constructed packet", data.data(),
                                       data.length(), AsChars(packet),
@@ -9878,12 +8762,9 @@
   payloads.push_back(payload2);
   size_t length = framer_.BuildPathResponsePacket(
       header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads,
-      /*is_padded=*/false);
+      /*is_padded=*/false, ENCRYPTION_NONE);
   EXPECT_EQ(length, QUIC_ARRAYSIZE(packet));
-  QuicPacket data(buffer.release(), length, true,
-                  header.destination_connection_id_length,
-                  header.source_connection_id_length, header.version_flag,
-                  header.nonce != nullptr, header.packet_number_length);
+  QuicPacket data(buffer.release(), length, true, header);
 
   test::CompareCharArraysWithHexError("constructed packet", data.data(),
                                       data.length(), AsChars(packet),
@@ -9933,12 +8814,9 @@
   payloads.push_back(payload2);
   size_t length = framer_.BuildPathResponsePacket(
       header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads,
-      /*is_padded=*/true);
+      /*is_padded=*/true, ENCRYPTION_NONE);
   EXPECT_EQ(length, QUIC_ARRAYSIZE(packet));
-  QuicPacket data(buffer.release(), length, true,
-                  header.destination_connection_id_length,
-                  header.source_connection_id_length, header.version_flag,
-                  header.nonce != nullptr, header.packet_number_length);
+  QuicPacket data(buffer.release(), length, true, header);
 
   test::CompareCharArraysWithHexError("constructed packet", data.data(),
                                       data.length(), AsChars(packet),
@@ -9962,18 +8840,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // frame type (ping frame)
-    0x07,
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id)
-    0x28,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // frame type (ping frame)
@@ -9992,7 +8858,7 @@
     0x07,
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short header, 4 byte packet number)
     0x43,
     // connection_id
@@ -10023,12 +8889,10 @@
   unsigned char* p = packet;
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   test::CompareCharArraysWithHexError(
@@ -10248,21 +9112,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // redundancy
-    'a',  'b',  'c',  'd',
-    'e',  'f',  'g',  'h',
-    'i',  'j',  'k',  'l',
-    'm',  'n',  'o',  'p',
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id)
-    0x28,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // redundancy
@@ -10287,7 +9136,7 @@
     'm',  'n',  'o',  'p',
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short header, 4 byte packet number)
     0x43,
     // connection_id
@@ -10321,18 +9170,17 @@
   unsigned char* p = packet;
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   std::unique_ptr<QuicPacket> raw(new QuicPacket(
       AsChars(p), QUIC_ARRAYSIZE(packet), false, PACKET_8BYTE_CONNECTION_ID,
       PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion,
-      !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER));
+      !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER,
+      VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0));
   char buffer[kMaxPacketSize];
   size_t encrypted_length = framer_.EncryptPayload(
       ENCRYPTION_NONE, packet_number, *raw, buffer, kMaxPacketSize);
@@ -10352,23 +9200,6 @@
     // version tag
     'Q', '.', '1', '0',
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // redundancy
-    'a',  'b',  'c',  'd',
-    'e',  'f',  'g',  'h',
-    'i',  'j',  'k',  'l',
-    'm',  'n',  'o',  'p',
-  };
-
-  unsigned char packet39[] = {
-    // public flags (version, 8 byte connection_id)
-    0x29,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // version tag
-    'Q', '.', '1', '0',
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // redundancy
@@ -10397,7 +9228,7 @@
     'm',  'n',  'o',  'p',
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (long header with packet type ZERO_RTT_PROTECTED)
     0xD3,
     // version tag
@@ -10439,12 +9270,10 @@
   unsigned char* p = packet;
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   std::unique_ptr<QuicPacket> raw(new QuicPacket(
@@ -10453,7 +9282,8 @@
                                                     : QUIC_ARRAYSIZE(packet),
       false, PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID,
       kIncludeVersion, !kIncludeDiversificationNonce,
-      PACKET_4BYTE_PACKET_NUMBER));
+      PACKET_4BYTE_PACKET_NUMBER, VARIABLE_LENGTH_INTEGER_LENGTH_0, 0,
+      VARIABLE_LENGTH_INTEGER_LENGTH_0));
   char buffer[kMaxPacketSize];
   size_t encrypted_length = framer_.EncryptPayload(
       ENCRYPTION_NONE, packet_number, *raw, buffer, kMaxPacketSize);
@@ -10594,43 +9424,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // frame type (stream frame with fin)
-    0xFF,
-    // stream id
-    0x04, 0x03, 0x02, 0x01,
-    // offset
-    0x54, 0x76, 0x10, 0x32,
-    0xDC, 0xFE, 0x98, 0x3A,
-    // data length
-    0x0c, 0x00,
-    // data
-    'h',  'e',  'l',  'l',
-    'o',  ' ',  'w',  'o',
-    'r',  'l',  'd',  '!',
-
-    // frame type (ack frame)
-    0x40,
-    // least packet number awaiting an ack
-    0xA0, 0x9A, 0x78, 0x56,
-    0x34, 0x12,
-    // largest observed packet number
-    0xBF, 0x9A, 0x78, 0x56,
-    0x34, 0x12,
-    // num missing packets
-    0x01,
-    // missing packet
-    0xBE, 0x9A, 0x78, 0x56,
-    0x34, 0x12,
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id)
-    0x28,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // frame type (stream frame with fin)
@@ -10699,7 +9492,7 @@
     0x9A, 0xBE,
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short header, 4 byte packet number)
     0x43,
     // connection_id
@@ -10790,14 +9583,12 @@
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
     p_size = QUIC_ARRAYSIZE(packet99);
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
-    p_size = QUIC_ARRAYSIZE(packet47);
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
+    p_size = QUIC_ARRAYSIZE(packet46);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
   QuicEncryptedPacket encrypted(AsChars(p), p_size, false);
   EXPECT_TRUE(framer_.ProcessPacket(encrypted));
@@ -10844,7 +9635,11 @@
   EXPECT_CALL(visitor, OnDecryptedPacket(_)).Times(1);
   EXPECT_CALL(visitor, OnError(_)).Times(0);
   EXPECT_CALL(visitor, OnStreamFrame(_)).Times(0);
-  EXPECT_CALL(visitor, OnStreamFrame(Truly(ExpectedStreamFrame))).Times(1);
+  if (framer_.version().transport_version < QUIC_VERSION_47) {
+    EXPECT_CALL(visitor, OnStreamFrame(Truly(ExpectedStreamFrame))).Times(1);
+  } else {
+    EXPECT_CALL(visitor, OnCryptoFrame(_)).Times(1);
+  }
   EXPECT_CALL(visitor, OnPacketComplete()).Times(1);
 
   EXPECT_TRUE(framer_.ProcessPacket(*packet));
@@ -10927,31 +9722,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-    // private flags
-    0x00,
-
-    // frame type (stream frame with fin)
-    0xFF,
-    // stream id
-    0x04, 0x03, 0x02, 0x01,
-    // offset
-    0x54, 0x76, 0x10, 0x32,
-    0xDC, 0xFE, 0x98, 0x3A,
-    // data length
-    0x0c, 0x00,
-    // data
-    'h',  'e',  'l',  'l',
-    'o',  ' ',  'w',  'o',
-    'r',  'l',  'd',  '!',
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id)
-    0x2C,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
     // private flags
     0x00,
@@ -10992,7 +9762,7 @@
     'r',  'l',  'd',  '!',
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short header, 4 byte packet number)
     0x43,
     // packet number
@@ -11038,12 +9808,10 @@
   unsigned char* p = packet;
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
   QuicFramerFuzzFunc(p,
                      framer_.transport_version() > QUIC_VERSION_43
@@ -11127,7 +9895,7 @@
   header.packet_number = kPacketNumber;
 
   QuicBlockedFrame frame;
-  frame.stream_id = 0;
+  frame.stream_id = QuicUtils::GetInvalidStreamId(framer_.transport_version());
   frame.offset = kStreamOffset;
   QuicFrames frames = {QuicFrame(&frame)};
 
@@ -11242,7 +10010,7 @@
                                       QUIC_ARRAYSIZE(packet99));
 }
 
-TEST_P(QuicFramerTest, MaxStreamIdFrame) {
+TEST_P(QuicFramerTest, ServerBiDiMaxStreamsFrame) {
   // This test only for version 99.
   if (framer_.transport_version() != QUIC_VERSION_99) {
     return;
@@ -11259,12 +10027,12 @@
       // packet number
       {"",
        {0x12, 0x34, 0x9A, 0xBC}},
-      // frame type (IETF_MAX_STREAM_ID)
+      // frame type (IETF_MAX_STREAMS_BIDIRECTIONAL)
       {"",
        {0x12}},
-      // max. stream id
-      {"Can not read MAX_STREAM_ID stream id.",
-       {kVarInt62OneByte + 0x01}},
+      // max. streams
+      {"Can not read MAX_STREAMS stream count.",
+       {kVarInt62OneByte + 0x03}},
   };
   // clang-format on
 
@@ -11278,26 +10046,173 @@
       *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
       PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
 
-  EXPECT_EQ(0x1u, visitor_.max_stream_id_frame_.max_stream_id);
-
+  // This test is a server receiving a MAX_STREAMS frame. The
+  // stream ID that it generates should be a server-initiated
+  // stream ID. The expected Stream ID is
+  //                 ((0x3-1) * 4) | 0x1 = 0x9
+  //                  count-to-id      server inited, bidi
+  EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER,
+                           /*bidirectional=*/true, 3),
+            visitor_.max_stream_id_frame_.max_stream_id);
   CheckFramingBoundaries(packet99, QUIC_MAX_STREAM_ID_DATA);
 }
 
-TEST_P(QuicFramerTest, BuildMaxStreamIdPacket) {
+TEST_P(QuicFramerTest, ClientBiDiMaxStreamsFrame) {
   // This test only for version 99.
   if (framer_.transport_version() != QUIC_VERSION_99) {
     return;
   }
 
-  QuicPacketHeader header;
-  header.destination_connection_id = FramerTestConnectionId();
-  header.reset_flag = false;
-  header.version_flag = false;
-  header.packet_number = kPacketNumber;
+  // clang-format off
+  PacketFragments packet99 = {
+      // type (short header, 4 byte packet number)
+      {"",
+       {0x43}},
+      // Test runs in client mode, no connection id
+      // packet number
+      {"",
+       {0x12, 0x34, 0x9A, 0xBC}},
+      // frame type (IETF_MAX_STREAMS_BIDIRECTIONAL)
+      {"",
+       {0x12}},
+      // max. streams
+      {"Can not read MAX_STREAMS stream count.",
+       {kVarInt62OneByte + 0x03}},
+  };
+  // clang-format on
 
-  QuicMaxStreamIdFrame frame;
-  frame.max_stream_id = kTestQuicStreamId;
-  QuicFrames frames = {QuicFrame(frame)};
+  std::unique_ptr<QuicEncryptedPacket> encrypted(
+      AssemblePacketFromFragments(packet99));
+  QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+  EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+  EXPECT_TRUE(CheckDecryption(
+      *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+      PACKET_0BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+  // This test is a client receiving a MAX_STREAMS frame. The
+  // stream ID that it generates should be a client-initiated
+  // stream ID. The expected Stream ID is
+  //                ((0x3-1) * 4)       = 0xc
+  // It is not 8 because a client-initiated, bidi stream ID's
+  // low bits are 00 - which means that the old crypto stream
+  // falls into this category, and the first stream is streamid=4,
+  // not streamid=0.
+  EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT,
+                           /*bidirectional=*/true, 3),
+            visitor_.max_stream_id_frame_.max_stream_id);
+
+  CheckFramingBoundaries(packet99, QUIC_MAX_STREAM_ID_DATA);
+}
+
+TEST_P(QuicFramerTest, ServerUniDiMaxStreamsFrame) {
+  // This test only for version 99.
+  if (framer_.transport_version() != QUIC_VERSION_99) {
+    return;
+  }
+
+  // clang-format off
+  PacketFragments packet99 = {
+      // type (short header, 4 byte packet number)
+      {"",
+       {0x43}},
+      // connection_id
+      {"",
+       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+      // packet number
+      {"",
+       {0x12, 0x34, 0x9A, 0xBC}},
+      // frame type (IETF_MAX_STREAMS_UNIDIRECTIONAL)
+      {"",
+       {0x13}},
+      // max. streams
+      {"Can not read MAX_STREAMS stream count.",
+       {kVarInt62OneByte + 0x03}},
+  };
+  // clang-format on
+
+  std::unique_ptr<QuicEncryptedPacket> encrypted(
+      AssemblePacketFromFragments(packet99));
+  EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+  EXPECT_TRUE(CheckDecryption(
+      *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+      PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+  // This test is a server receiving a MAX_STREAMS frame. The
+  // stream ID that it generates should be a server-initiated
+  // stream ID. The expected Stream ID is
+  //      ((0x3-1) * 4) | 0x1 | 0x2 = 0xb
+  //        count-to-id      server inited, unidi
+  EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER,
+                           /*bidirectional=*/false, 3),
+            visitor_.max_stream_id_frame_.max_stream_id);
+
+  CheckFramingBoundaries(packet99, QUIC_MAX_STREAM_ID_DATA);
+}
+
+TEST_P(QuicFramerTest, ClientUniDiMaxStreamsFrame) {
+  // This test only for version 99.
+  if (framer_.transport_version() != QUIC_VERSION_99) {
+    return;
+  }
+
+  // clang-format off
+  PacketFragments packet99 = {
+      // type (short header, 4 byte packet number)
+      {"",
+       {0x43}},
+      // Test runs in client mode, no connection id
+      // packet number
+      {"",
+       {0x12, 0x34, 0x9A, 0xBC}},
+      // frame type (IETF_MAX_STREAMS_UNIDIRECTIONAL)
+      {"",
+       {0x13}},
+      // max. streams
+      {"Can not read MAX_STREAMS stream count.",
+       {kVarInt62OneByte + 0x03}},
+  };
+  // clang-format on
+
+  std::unique_ptr<QuicEncryptedPacket> encrypted(
+      AssemblePacketFromFragments(packet99));
+  QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+  EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+  EXPECT_TRUE(CheckDecryption(
+      *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+      PACKET_0BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+  // This test is a client receiving a MAX_STREAMS frame. The
+  // stream ID that it generates should be a client-initiated
+  // stream ID. The expected Stream ID is
+  //               ((0x3-1) * 4) | 0x02= 0xa
+  //                count-to-id      client/unidi
+  EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT,
+                           /*bidirectional=*/false, 3),
+            visitor_.max_stream_id_frame_.max_stream_id);
+
+  CheckFramingBoundaries(packet99, QUIC_MAX_STREAM_ID_DATA);
+}
+
+// The following four tests ensure that the framer can deserialize a stream
+// count that is large enough to cause the resulting stream ID to exceed the
+// current implementation limit(32 bits). The intent is that when this happens,
+// the stream limit is pegged to the maximum supported value. There are four
+// tests, for the four combinations of uni- and bi-directional, server- and
+// client- initiated.
+TEST_P(QuicFramerTest, ServerBiDiMaxStreamsFrameTooBig) {
+  // This test only for version 99.
+  if (framer_.transport_version() != QUIC_VERSION_99) {
+    return;
+  }
 
   // clang-format off
   unsigned char packet99[] = {
@@ -11306,24 +10221,206 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x12, 0x34, 0x56, 0x78,
-
-    // frame type (IETF_MAX_STREAM_ID frame)
+    0x12, 0x34, 0x9A, 0xBC,
+    // frame type (IETF_MAX_STREAMS_BIDIRECTIONAL)
     0x12,
-    // Max stream id
-    kVarInt62OneByte + 0x01
+
+    // max. streams. Max stream ID allowed is 0xffffffff
+    // This encodes a count of 0x40000000, leading to stream
+    // IDs in the range 0x1 00000000 to 0x1 00000003.
+    kVarInt62EightBytes + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00
   };
   // clang-format on
 
-  std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
-  ASSERT_TRUE(data != nullptr);
+  QuicEncryptedPacket encrypted(AsChars(packet99), QUIC_ARRAYSIZE(packet99),
+                                false);
+  EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+  EXPECT_TRUE(CheckDecryption(
+      encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+      PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
 
-  test::CompareCharArraysWithHexError("constructed packet", data->data(),
-                                      data->length(), AsChars(packet99),
-                                      QUIC_ARRAYSIZE(packet99));
+  // This test is a server receiving a MAX_STREAMS frame. The
+  // stream ID that it generates should be a server-initiated
+  // stream ID. The expected Stream ID is
+  //            0xfffffffc | 0x01  --> 0xfffffffd
+  //              maxid     server inited, bidi
+  EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER,
+                           /*bidirectional=*/true, 0x40000000),
+            visitor_.max_stream_id_frame_.max_stream_id);
 }
 
-TEST_P(QuicFramerTest, StreamIdBlockedFrame) {
+TEST_P(QuicFramerTest, ClientBiDiMaxStreamsFrameTooBig) {
+  // This test only for version 99.
+  if (framer_.transport_version() != QUIC_VERSION_99) {
+    return;
+  }
+
+  // clang-format off
+  unsigned char packet99[] = {
+    // type (short header, 4 byte packet number)
+    0x43,
+    // Test runs in client mode, no connection id
+    // packet number
+    0x12, 0x34, 0x9A, 0xBC,
+    // frame type (IETF_MAX_STREAMS_BIDIRECTIONAL)
+    0x12,
+
+    // max. streams. Max stream ID allowed is 0xffffffff
+    // This encodes a count of 0x40000000, leading to stream
+    // IDs in the range 0x1 00000000 to 0x1 00000003.
+    kVarInt62EightBytes + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00
+  };
+  // clang-format on
+
+  QuicEncryptedPacket encrypted(AsChars(packet99), QUIC_ARRAYSIZE(packet99),
+                                false);
+  QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+  EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+  EXPECT_TRUE(CheckDecryption(
+      encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+      PACKET_0BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+  // This test is a client receiving a MAX_STREAMS frame. The
+  // stream ID that it generates should be a client-initiated
+  // stream ID. The expected Stream ID is
+  //            0xfffffffc         --> 0xfffffffc
+  //            max id       bidi/client-inited
+  // TODO(fkastenholz): Change -2 to -1 when stream id 0 is no longer
+  // special.
+  // Subtract 1 because client/bidi stream ids start counting at
+  // 4, not 0. If we didn;t subtract 1, the resulting math would wrap to stream
+  // id 0, not 0xfffffffc.
+  EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT,
+                           /*bidirectional=*/true, (0x40000000 - 1)),
+            visitor_.max_stream_id_frame_.max_stream_id);
+}
+
+TEST_P(QuicFramerTest, ServerUniDiMaxStreamsFrameTooBig) {
+  // This test only for version 99.
+  if (framer_.transport_version() != QUIC_VERSION_99) {
+    return;
+  }
+
+  // clang-format off
+  unsigned char packet99[] = {
+    // type (short header, 4 byte packet number)
+    0x43,
+    // connection_id
+    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+    // packet number
+    0x12, 0x34, 0x9A, 0xBC,
+    // frame type (IETF_MAX_STREAMS_UNIDIRECTIONAL)
+    0x13,
+
+    // max. streams. Max stream ID allowed is 0xffffffff
+    // This encodes a count of 0x40000000, leading to stream
+    // IDs in the range 0x1 00000000 to 0x1 00000003.
+    kVarInt62EightBytes + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00
+  };
+  // clang-format on
+
+  QuicEncryptedPacket encrypted(AsChars(packet99), QUIC_ARRAYSIZE(packet99),
+                                false);
+  EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+  EXPECT_TRUE(CheckDecryption(
+      encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+      PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+  // This test is a server receiving a MAX_STREAMS frame. The
+  // stream ID that it generates should be a server-initiated
+  // stream ID. The expected Stream ID is
+  //      0xfffffffc | 0x1 | 0x2 = 0xffffffff
+  //        maxid      server inited, unidi
+  EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER,
+                           /*bidirectional=*/false, 0x40000000),
+            visitor_.max_stream_id_frame_.max_stream_id);
+}
+
+TEST_P(QuicFramerTest, ClientUniDiMaxStreamsFrameTooBig) {
+  // This test only for version 99.
+  if (framer_.transport_version() != QUIC_VERSION_99) {
+    return;
+  }
+
+  // clang-format off
+  unsigned char packet99[] = {
+    // type (short header, 4 byte packet number)
+    0x43,
+    // Test runs in client mode, no connection id
+    // packet number
+    0x12, 0x34, 0x9A, 0xBC,
+    // frame type (IETF_MAX_STREAMS_UNDIRECTIONAL)
+    0x13,
+
+    // max. streams. Max stream ID allowed is 0xffffffff
+    // This encodes a count of 0x40000000, leading to stream
+    // IDs in the range 0x1 00000000 to 0x1 00000003.
+    kVarInt62EightBytes + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00
+  };
+  // clang-format on
+
+  QuicEncryptedPacket encrypted(AsChars(packet99), QUIC_ARRAYSIZE(packet99),
+                                false);
+  QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+  EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+  EXPECT_TRUE(CheckDecryption(
+      encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+      PACKET_0BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+  // This test is a client receiving a MAX_STREAMS frame. The
+  // stream ID that it generates should be a client-initiated
+  // stream ID. The expected Stream ID is
+  //               0xfffffffc | 0x02= 0xfffffffe
+  //                maxid       client/unidi
+  EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT,
+                           /*bidirectional=*/false, 0x40000000),
+            visitor_.max_stream_id_frame_.max_stream_id);
+}
+
+// Check that a stream count of 0 is rejected.
+// Directionality and intiation are not important for
+// this test.
+TEST_P(QuicFramerTest, MaxStreamsFrameZeroCount) {
+  // This test only for version 99.
+  if (framer_.transport_version() != QUIC_VERSION_99) {
+    return;
+  }
+
+  // clang-format off
+  unsigned char packet99[] = {
+    // type (short header, 4 byte packet number)
+    0x43,
+    // connection_id
+    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+    // packet number
+    0x12, 0x34, 0x9A, 0xBC,
+    // frame type (IETF_MAX_STREAMS_BIDIRECTIONAL)
+    0x12,
+    // max. streams == 0.
+    kVarInt62OneByte + 0x00
+  };
+  // clang-format on
+
+  QuicEncryptedPacket encrypted(AsChars(packet99), QUIC_ARRAYSIZE(packet99),
+                                false);
+  EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+  EXPECT_EQ(QUIC_MAX_STREAM_ID_DATA, framer_.error());
+  EXPECT_EQ(framer_.detailed_error(),
+            "MAX_STREAMS stream count of 0 not supported.");
+}
+
+TEST_P(QuicFramerTest, ServerBiDiStreamsBlockedFrame) {
   // This test only for version 99.
   if (framer_.transport_version() != QUIC_VERSION_99) {
     return;
@@ -11340,12 +10437,12 @@
       // packet number
       {"",
        {0x12, 0x34, 0x9A, 0xBC}},
-      // frame type (IETF_STREAM_ID_BLOCKED frame)
+      // frame type (IETF_STREAMS_BLOCKED_BIDIRECTIONAL frame)
       {"",
        {0x16}},
       // stream id
-      {"Can not read STREAM_ID_BLOCKED stream id.",
-       {kVarInt62OneByte + 0x01}},
+      {"Can not read STREAMS_BLOCKED stream id.",
+       {kVarInt62OneByte + 0x03}},
   };
   // clang-format on
 
@@ -11359,12 +10456,234 @@
       *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
       PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
 
-  EXPECT_EQ(0x1u, visitor_.stream_id_blocked_frame_.stream_id);
+  // This test is a server receiving a STREAMS_BLOCKED frame. The
+  // stream ID that it generates should be a client-initiated
+  // stream ID. The expected Stream ID is
+  //                ((0x3-1) * 4)        = 0xc
+  //                 count-to-id      client inited, bidi
+  // It is not 8 because a client-initiated, bidi stream ID's
+  // low bits are 00 - which means that the old crypto stream
+  // falls into this category, and the first stream is streamid=4,
+  // not streamid=0.
+  EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT,
+                           /*bidirectional=*/true, 3),
+            visitor_.stream_id_blocked_frame_.stream_id);
 
   CheckFramingBoundaries(packet99, QUIC_STREAM_ID_BLOCKED_DATA);
 }
 
-TEST_P(QuicFramerTest, BuildStreamIdBlockedPacket) {
+TEST_P(QuicFramerTest, ClientBiDiStreamsBlockedFrame) {
+  // This test only for version 99.
+  if (framer_.transport_version() != QUIC_VERSION_99) {
+    return;
+  }
+
+  // clang-format off
+  PacketFragments packet99 = {
+      // type (short header, 4 byte packet number)
+      {"",
+       {0x43}},
+      // Test runs in client mode, no connection id
+      // packet number
+      {"",
+       {0x12, 0x34, 0x9A, 0xBC}},
+      // frame type (IETF_STREAMS_BLOCKED_BIDIRECTIONAL frame)
+      {"",
+       {0x16}},
+      // stream id
+      {"Can not read STREAMS_BLOCKED stream id.",
+       {kVarInt62OneByte + 0x03}},
+  };
+  // clang-format on
+
+  std::unique_ptr<QuicEncryptedPacket> encrypted(
+      AssemblePacketFromFragments(packet99));
+  QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+  EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+  EXPECT_TRUE(CheckDecryption(
+      *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+      PACKET_0BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+  // This test is a client receiving a STREAMS_BLOCKED frame. The
+  // stream ID that it generates should be a server-initiated
+  // stream ID. The expected Stream ID is
+  //                ((0x3-1) * 4) | 0x01 = 0x9
+  //                 count-to-id      server inited, bidi
+  EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER,
+                           /*bidirectional=*/true, 3),
+            visitor_.stream_id_blocked_frame_.stream_id);
+
+  CheckFramingBoundaries(packet99, QUIC_STREAM_ID_BLOCKED_DATA);
+}
+
+TEST_P(QuicFramerTest, ServerUniDiStreamsBlockedFrame) {
+  // This test only for version 99.
+  if (framer_.transport_version() != QUIC_VERSION_99) {
+    return;
+  }
+
+  // clang-format off
+  PacketFragments packet99 = {
+      // type (short header, 4 byte packet number)
+      {"",
+       {0x43}},
+      // connection_id
+      {"",
+       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+      // packet number
+      {"",
+       {0x12, 0x34, 0x9A, 0xBC}},
+      // frame type (IETF_STREAMS_BLOCKED_UNIDIRECTIONAL frame)
+      {"",
+       {0x17}},
+      // stream id
+      {"Can not read STREAMS_BLOCKED stream id.",
+       {kVarInt62OneByte + 0x03}},
+  };
+  // clang-format on
+
+  std::unique_ptr<QuicEncryptedPacket> encrypted(
+      AssemblePacketFromFragments(packet99));
+  EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+  EXPECT_TRUE(CheckDecryption(
+      *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+      PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+  // This test is a server receiving a STREAMS_BLOCKED frame. The
+  // stream ID that it generates should be a client-initiated
+  // stream ID. The expected Stream ID is
+  //                ((0x3-1) * 4)  | 0x2  = 0xa
+  //                 count-to-id      client inited, unidi
+  EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT,
+                           /*bidirectional=*/false, 3),
+            visitor_.stream_id_blocked_frame_.stream_id);
+
+  CheckFramingBoundaries(packet99, QUIC_STREAM_ID_BLOCKED_DATA);
+}
+
+TEST_P(QuicFramerTest, ClientUniDiStreamsBlockedFrame) {
+  // This test only for version 99.
+  if (framer_.transport_version() != QUIC_VERSION_99) {
+    return;
+  }
+
+  // clang-format off
+  PacketFragments packet99 = {
+      // type (short header, 4 byte packet number)
+      {"",
+       {0x43}},
+      // Test runs in client mode, no connection id
+      // packet number
+      {"",
+       {0x12, 0x34, 0x9A, 0xBC}},
+      // frame type (IETF_STREAMS_BLOCKED_UNIDIRECTIONAL frame)
+      {"",
+       {0x17}},
+      // stream id
+      {"Can not read STREAMS_BLOCKED stream id.",
+       {kVarInt62OneByte + 0x03}},
+  };
+  // clang-format on
+
+  std::unique_ptr<QuicEncryptedPacket> encrypted(
+      AssemblePacketFromFragments(packet99));
+  QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+  EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+  EXPECT_TRUE(CheckDecryption(
+      *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+      PACKET_0BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+  // This test is a client receiving a STREAMS_BLOCKED frame. The
+  // stream ID that it generates should be a server-initiated
+  // stream ID. The expected Stream ID is
+  //                ((0x3-1) * 4) | 0x01 | 0x2 = 0xb
+  //                 count-to-id      server inited, bidi
+  EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER,
+                           /*bidirectional=*/false, 3),
+            visitor_.stream_id_blocked_frame_.stream_id);
+
+  CheckFramingBoundaries(packet99, QUIC_STREAM_ID_BLOCKED_DATA);
+}
+
+// Check that when we get a STREAMS_BLOCKED frame that specifies too large
+// a stream count, we reject with an appropriate error. There is no need to
+// check for different combinations of Uni/Bi directional and client/server
+// initiated; the logic does not take these into account.
+TEST_P(QuicFramerTest, StreamsBlockedFrameTooBig) {
+  // This test only for version 99.
+  if (framer_.transport_version() != QUIC_VERSION_99) {
+    return;
+  }
+
+  // clang-format off
+  unsigned char packet99[] = {
+    // type (short header, 4 byte packet number)
+    0x43,
+    // Test runs in client mode, no connection id
+    // packet number
+    0x12, 0x34, 0x9A, 0xBC,
+    // frame type (IETF_STREAMS_BLOCKED_BIDIRECTIONAL)
+    0x17,
+
+    // max. streams. Max stream ID allowed is 0xffffffff
+    // This encodes a count of 0x40000000, leading to stream
+    // IDs in the range 0x1 00000000 to 0x1 00000003.
+    kVarInt62EightBytes + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01
+  };
+  // clang-format on
+
+  QuicEncryptedPacket encrypted(AsChars(packet99), QUIC_ARRAYSIZE(packet99),
+                                false);
+  QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+  EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+
+  EXPECT_EQ(QUIC_STREAM_ID_BLOCKED_DATA, framer_.error());
+  EXPECT_EQ(framer_.detailed_error(),
+            "STREAMS_BLOCKED stream count exceeds implementation limit.");
+}
+
+// Test that count==0 is rejected.
+TEST_P(QuicFramerTest, StreamsBlockedFrameZeroCount) {
+  // This test only for version 99.
+  if (framer_.transport_version() != QUIC_VERSION_99) {
+    return;
+  }
+
+  // clang-format off
+  unsigned char packet99[] = {
+    // type (short header, 4 byte packet number)
+    0x43,
+    // Test runs in client mode, no connection id
+    // packet number
+    0x12, 0x34, 0x9A, 0xBC,
+    // frame type (IETF_STREAMS_BLOCKED_BIDIRECTIONAL)
+    0x17,
+
+    // max. streams = 0
+    kVarInt62OneByte + 0x00
+  };
+  // clang-format on
+
+  QuicEncryptedPacket encrypted(AsChars(packet99), QUIC_ARRAYSIZE(packet99),
+                                false);
+  QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+  EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+
+  EXPECT_EQ(QUIC_STREAM_ID_BLOCKED_DATA, framer_.error());
+  EXPECT_EQ(framer_.detailed_error(),
+            "STREAMS_BLOCKED stream count 0 not supported.");
+}
+
+TEST_P(QuicFramerTest, BuildServerBiDiStreamsBlockedPacket) {
   // This test only for version 99.
   if (framer_.transport_version() != QUIC_VERSION_99) {
     return;
@@ -11377,7 +10696,13 @@
   header.packet_number = kPacketNumber;
 
   QuicStreamIdBlockedFrame frame;
-  frame.stream_id = kTestQuicStreamId;
+  // A server building a STREAMS_BLOCKED frame generates
+  // a server-initiated stream ID. This test is bidirectional.
+  // The low two bits of the stream ID are 01
+  // Expected value is 0x8u | 0x1u;
+  frame.stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER,
+                                   /*bidirectional=*/true, 3);
+
   QuicFrames frames = {QuicFrame(frame)};
 
   // clang-format off
@@ -11389,10 +10714,332 @@
     // packet number
     0x12, 0x34, 0x56, 0x78,
 
-    // frame type (IETF_STREAM_ID_BLOCKED frame)
+    // frame type (IETF_STREAMS_BLOCKED_BIDIRECTIONAL frame)
     0x16,
-    // Max stream id
-    kVarInt62OneByte + 0x01
+    // Stream count
+    kVarInt62OneByte + 0x03
+  };
+  // clang-format on
+
+  std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+  ASSERT_TRUE(data != nullptr);
+
+  test::CompareCharArraysWithHexError("constructed packet", data->data(),
+                                      data->length(), AsChars(packet99),
+                                      QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, BuildClientBiDiStreamsBlockedPacket) {
+  // This test only for version 99.
+  if (framer_.transport_version() != QUIC_VERSION_99) {
+    return;
+  }
+
+  // This test runs in client mode.
+  QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+
+  QuicPacketHeader header;
+  header.destination_connection_id = FramerTestConnectionId();
+  header.reset_flag = false;
+  header.version_flag = false;
+  header.packet_number = kPacketNumber;
+
+  QuicStreamIdBlockedFrame frame;
+  // A client building a STREAMS_BLOCKED frame generates
+  // a client-initiated stream ID. This test is bidirectional.
+  // The low two bits of the stream ID are 00. Expected value is 0x8
+  frame.stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT,
+                                   /*bidirectional=*/true, 3);
+  QuicFrames frames = {QuicFrame(frame)};
+
+  // clang-format off
+  unsigned char packet99[] = {
+    // type (short header, 4 byte packet number)
+    0x43,
+    // connection_id
+    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+    // packet number
+    0x12, 0x34, 0x56, 0x78,
+
+    // frame type (IETF_STREAMS_BLOCKED_BIDIRECTIONAL frame)
+    0x16,
+    // Stream count
+    kVarInt62OneByte + 0x03
+  };
+  // clang-format on
+
+  std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+  ASSERT_TRUE(data != nullptr);
+
+  test::CompareCharArraysWithHexError("constructed packet", data->data(),
+                                      data->length(), AsChars(packet99),
+                                      QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, BuildServerUniStreamsBlockedPacket) {
+  // This test only for version 99.
+  if (framer_.transport_version() != QUIC_VERSION_99) {
+    return;
+  }
+
+  QuicPacketHeader header;
+  header.destination_connection_id = FramerTestConnectionId();
+  header.reset_flag = false;
+  header.version_flag = false;
+  header.packet_number = kPacketNumber;
+
+  QuicStreamIdBlockedFrame frame;
+  // A server building a STREAMS_BLOCKED frame generates
+  // a server-initiated stream ID. This test is bidirectional.
+  // The low two bits of the stream ID are 11. Expected value is 0xb
+  frame.stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER,
+                                   /*bidirectional=*/false, 3);
+  QuicFrames frames = {QuicFrame(frame)};
+
+  // clang-format off
+  unsigned char packet99[] = {
+    // type (short header, 4 byte packet number)
+    0x43,
+    // connection_id
+    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+    // packet number
+    0x12, 0x34, 0x56, 0x78,
+
+    // frame type (IETF_STREAMS_BLOCKED_UNIDIRECTIONAL frame)
+    0x17,
+    // Stream count
+    kVarInt62OneByte + 0x03
+  };
+  // clang-format on
+
+  std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+  ASSERT_TRUE(data != nullptr);
+
+  test::CompareCharArraysWithHexError("constructed packet", data->data(),
+                                      data->length(), AsChars(packet99),
+                                      QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, BuildClientUniDiStreamsBlockedPacket) {
+  // This test only for version 99.
+  if (framer_.transport_version() != QUIC_VERSION_99) {
+    return;
+  }
+
+  // This test runs in client mode.
+  QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+
+  QuicPacketHeader header;
+  header.destination_connection_id = FramerTestConnectionId();
+  header.reset_flag = false;
+  header.version_flag = false;
+  header.packet_number = kPacketNumber;
+
+  QuicStreamIdBlockedFrame frame;
+  // A client building a STREAMS_BLOCKED frame generates
+  // a client-initiated stream ID. This test is bidirectional.
+  // The low two bits of the stream ID are 10. Expected value is 0xa
+  frame.stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT,
+                                   /*bidirectional=*/false, 3);
+  QuicFrames frames = {QuicFrame(frame)};
+
+  // clang-format off
+  unsigned char packet99[] = {
+    // type (short header, 4 byte packet number)
+    0x43,
+    // connection_id
+    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+    // packet number
+    0x12, 0x34, 0x56, 0x78,
+
+    // frame type (IETF_STREAMS_BLOCKED_UNIDIRECTIONAL frame)
+    0x17,
+    // Stream count
+    kVarInt62OneByte + 0x03
+  };
+  // clang-format on
+
+  std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+  ASSERT_TRUE(data != nullptr);
+
+  test::CompareCharArraysWithHexError("constructed packet", data->data(),
+                                      data->length(), AsChars(packet99),
+                                      QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, BuildServerBiDiMaxStreamsPacket) {
+  // This test only for version 99.
+  if (framer_.transport_version() != QUIC_VERSION_99) {
+    return;
+  }
+
+  QuicPacketHeader header;
+  header.destination_connection_id = FramerTestConnectionId();
+  header.reset_flag = false;
+  header.version_flag = false;
+  header.packet_number = kPacketNumber;
+
+  QuicMaxStreamIdFrame frame;
+  // A server building a MAX_STREAMS frame generates
+  // a client-initiated stream ID. This test is bidirectional.
+  // The low two bits of the stream ID are 00. Expected value is 0xc
+  // because streamid==0 is special and the first client/bidi
+  // stream is 4, not 0.
+  frame.max_stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT,
+                                       /*bidirectional=*/true, 3);
+  QuicFrames frames = {QuicFrame(frame)};
+
+  // clang-format off
+  unsigned char packet99[] = {
+    // type (short header, 4 byte packet number)
+    0x43,
+    // connection_id
+    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+    // packet number
+    0x12, 0x34, 0x56, 0x78,
+
+    // frame type (IETF_MAX_STREAMS_BIDIRECTIONAL frame)
+    0x12,
+    // Stream count
+    kVarInt62OneByte + 0x03
+  };
+  // clang-format on
+
+  std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+  ASSERT_TRUE(data != nullptr);
+
+  test::CompareCharArraysWithHexError("constructed packet", data->data(),
+                                      data->length(), AsChars(packet99),
+                                      QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, BuildClientBiDiMaxStreamsPacket) {
+  // This test only for version 99.
+  if (framer_.transport_version() != QUIC_VERSION_99) {
+    return;
+  }
+
+  // This test runs in client mode.
+  QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+
+  QuicPacketHeader header;
+  header.destination_connection_id = FramerTestConnectionId();
+  header.reset_flag = false;
+  header.version_flag = false;
+  header.packet_number = kPacketNumber;
+
+  QuicMaxStreamIdFrame frame;
+  // A client building a MAX_STREAMS frame generates
+  // a server-initiated stream ID. This test is bidirectional.
+  // The low two bits of the stream ID are 01. Expected value is 0x9
+  frame.max_stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER,
+                                       /*bidirectional=*/true, 3);
+  QuicFrames frames = {QuicFrame(frame)};
+
+  // clang-format off
+  unsigned char packet99[] = {
+    // type (short header, 4 byte packet number)
+    0x43,
+    // connection_id
+    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+    // packet number
+    0x12, 0x34, 0x56, 0x78,
+
+    // frame type (IETF_MAX_STREAMS_BIDIRECTIONAL frame)
+    0x12,
+    // Stream count
+    kVarInt62OneByte + 0x03
+  };
+  // clang-format on
+
+  std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+  ASSERT_TRUE(data != nullptr);
+
+  test::CompareCharArraysWithHexError("constructed packet", data->data(),
+                                      data->length(), AsChars(packet99),
+                                      QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, BuildServerUniMaxStreamsPacket) {
+  // This test only for version 99.
+  if (framer_.transport_version() != QUIC_VERSION_99) {
+    return;
+  }
+
+  QuicPacketHeader header;
+  header.destination_connection_id = FramerTestConnectionId();
+  header.reset_flag = false;
+  header.version_flag = false;
+  header.packet_number = kPacketNumber;
+
+  QuicMaxStreamIdFrame frame;
+  // A server building a MAX_STREAMS frame generates
+  // a client-initiated stream ID. This test is bidirectional.
+  // The low two bits of the stream ID are 10. Expected value is 0xa
+  frame.max_stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT,
+                                       /*bidirectional=*/false, 3);
+  QuicFrames frames = {QuicFrame(frame)};
+
+  // clang-format off
+  unsigned char packet99[] = {
+    // type (short header, 4 byte packet number)
+    0x43,
+    // connection_id
+    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+    // packet number
+    0x12, 0x34, 0x56, 0x78,
+
+    // frame type (IETF_MAX_STREAMS_UNIDIRECTIONAL frame)
+    0x13,
+    // Stream count
+    kVarInt62OneByte + 0x03
+  };
+  // clang-format on
+
+  std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+  ASSERT_TRUE(data != nullptr);
+
+  test::CompareCharArraysWithHexError("constructed packet", data->data(),
+                                      data->length(), AsChars(packet99),
+                                      QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, BuildClientUniDiMaxStreamsPacket) {
+  // This test only for version 99.
+  if (framer_.transport_version() != QUIC_VERSION_99) {
+    return;
+  }
+
+  // This test runs in client mode.
+  QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+
+  QuicPacketHeader header;
+  header.destination_connection_id = FramerTestConnectionId();
+  header.reset_flag = false;
+  header.version_flag = false;
+  header.packet_number = kPacketNumber;
+
+  QuicMaxStreamIdFrame frame;
+  // A client building a MAX_STREAMS frame generates
+  // a server-initiated stream ID. This test is bidirectional.
+  // The low two bits of the stream ID are 11. Expected value is 0xb
+  frame.max_stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER,
+                                       /*bidirectional=*/false, 3);
+  QuicFrames frames = {QuicFrame(frame)};
+
+  // clang-format off
+  unsigned char packet99[] = {
+    // type (short header, 4 byte packet number)
+    0x43,
+    // connection_id
+    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+    // packet number
+    0x12, 0x34, 0x56, 0x78,
+
+    // frame type (IETF_MAX_STREAMS_UNIDIRECTIONAL frame)
+    0x13,
+    // Stream count
+    kVarInt62OneByte + 0x03
   };
   // clang-format on
 
@@ -11901,14 +11548,14 @@
                 framer_.transport_version(), QuicFrame(&new_connection_id)));
 
   QuicMaxStreamIdFrame max_stream_id(6, 3);
-  EXPECT_EQ(QuicFramer::GetMaxStreamIdFrameSize(framer_.transport_version(),
-                                                max_stream_id),
+  EXPECT_EQ(QuicFramer::GetMaxStreamsFrameSize(framer_.transport_version(),
+                                               max_stream_id),
             QuicFramer::GetRetransmittableControlFrameSize(
                 framer_.transport_version(), QuicFrame(max_stream_id)));
 
   QuicStreamIdBlockedFrame stream_id_blocked(7, 3);
-  EXPECT_EQ(QuicFramer::GetStreamIdBlockedFrameSize(framer_.transport_version(),
-                                                    stream_id_blocked),
+  EXPECT_EQ(QuicFramer::GetStreamsBlockedFrameSize(framer_.transport_version(),
+                                                   stream_id_blocked),
             QuicFramer::GetRetransmittableControlFrameSize(
                 framer_.transport_version(), QuicFrame(stream_id_blocked)));
 
@@ -12658,26 +12305,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // frame type (ack frame)
-    0x45,
-    // largest observed
-    0x00, 0x00,
-    // Zero delta time.
-    0x00, 0x00,
-    // first ack block length.
-    0x00, 0x00,
-    // num timestamps.
-    0x00
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id)
-    0x2C,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // frame type (ack frame)
@@ -12712,7 +12339,7 @@
     0x00
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short header, 4 byte packet number)
     0x43,
     // connection_id
@@ -12758,21 +12385,14 @@
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
     p_size = QUIC_ARRAYSIZE(packet99);
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   QuicEncryptedPacket encrypted(AsChars(p), p_size, false);
-  if (!GetQuicReloadableFlag(quic_disallow_peer_ack_0) &&
-      framer_.transport_version() != QUIC_VERSION_99) {
-    EXPECT_TRUE(framer_.ProcessPacket(encrypted));
-    return;
-  }
   EXPECT_FALSE(framer_.ProcessPacket(encrypted));
   EXPECT_EQ(framer_.detailed_error(), "Largest acked is 0.");
 }
@@ -12785,26 +12405,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // frame type (ack frame)
-    0x45,
-    // largest observed
-    0x02, 0x00,
-    // Zero delta time.
-    0x00, 0x00,
-    // first ack block length.
-    0x03, 0x00,
-    // num timestamps.
-    0x00
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id)
-    0x2C,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // frame type (ack frame)
@@ -12839,7 +12439,7 @@
     0x00
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short header, 4 byte packet number)
     0x43,
     // connection_id
@@ -12885,22 +12485,15 @@
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
     p_size = QUIC_ARRAYSIZE(packet99);
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
-    p_size = QUIC_ARRAYSIZE(packet47);
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
+    p_size = QUIC_ARRAYSIZE(packet46);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   QuicEncryptedPacket encrypted(AsChars(p), p_size, false);
-  if (!GetQuicReloadableFlag(quic_disallow_peer_ack_0) &&
-      framer_.transport_version() != QUIC_VERSION_99) {
-    EXPECT_TRUE(framer_.ProcessPacket(encrypted));
-    return;
-  }
   EXPECT_FALSE(framer_.ProcessPacket(encrypted));
   EXPECT_EQ(framer_.detailed_error(),
             "Underflow with first ack block length 3 largest acked is 2.");
@@ -12914,36 +12507,6 @@
     // connection_id
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // packet number
-    0x78, 0x56, 0x34, 0x12,
-
-    // frame type (ack frame)
-    0x60,
-    // largest observed
-    0x0A,
-    // Zero delta time.
-    0x00, 0x00,
-    // Num of ack blocks
-    0x02,
-    // first ack block length.
-    0x02,
-    // gap to next block
-    0x01,
-    // ack block length
-    0x01,
-    // gap to next block
-    0x01,
-    // ack block length
-    0x06,
-    // num timestamps.
-    0x00
-  };
-
-  unsigned char packet39[] = {
-    // public flags (8 byte connection_id)
-    0x2C,
-    // connection_id
-    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
-    // packet number
     0x12, 0x34, 0x56, 0x78,
 
     // frame type (ack frame)
@@ -12998,7 +12561,7 @@
     0x00
   };
 
-  unsigned char packet47[] = {
+  unsigned char packet46[] = {
     // type (short header, 4 byte packet number)
     0x43,
     // connection_id
@@ -13062,22 +12625,15 @@
   if (framer_.transport_version() == QUIC_VERSION_99) {
     p = packet99;
     p_size = QUIC_ARRAYSIZE(packet99);
-  } else if (framer_.transport_version() > QUIC_VERSION_46) {
-    p = packet47;
-    p_size = QUIC_ARRAYSIZE(packet47);
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
+    p = packet46;
+    p_size = QUIC_ARRAYSIZE(packet46);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
-  } else if (framer_.transport_version() != QUIC_VERSION_35) {
-    p = packet39;
   }
 
   QuicEncryptedPacket encrypted(AsChars(p), p_size, false);
-  if (!GetQuicReloadableFlag(quic_disallow_peer_ack_0) &&
-      framer_.transport_version() != QUIC_VERSION_99) {
-    EXPECT_TRUE(framer_.ProcessPacket(encrypted));
-    return;
-  }
   EXPECT_FALSE(framer_.ProcessPacket(encrypted));
   if (framer_.transport_version() == QUIC_VERSION_99) {
     EXPECT_EQ(framer_.detailed_error(),
@@ -13088,6 +12644,242 @@
   }
 }
 
+TEST_P(QuicFramerTest, CoalescedPacket) {
+  if (!QuicVersionHasLongHeaderLengths(framer_.transport_version())) {
+    return;
+  }
+  // clang-format off
+  unsigned char packet[] = {
+    // first coalesced packet
+      // public flags (long header with packet type ZERO_RTT_PROTECTED and
+      // 4-byte packet number)
+      0xD3,
+      // version
+      QUIC_VERSION_BYTES,
+      // destination connection ID length
+      0x50,
+      // destination connection ID
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // long header packet length
+      0x1E,
+      // packet number
+      0x12, 0x34, 0x56, 0x78,
+      // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set)
+      0x08 | 0x01 | 0x02 | 0x04,
+      // stream id
+      kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04,
+      // offset
+      kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+      0x32, 0x10, 0x76, 0x54,
+      // data length
+      kVarInt62OneByte + 0x0c,
+      // data
+      'h',  'e',  'l',  'l',
+      'o',  ' ',  'w',  'o',
+      'r',  'l',  'd',  '!',
+    // second coalesced packet
+      // public flags (long header with packet type ZERO_RTT_PROTECTED and
+      // 4-byte packet number)
+      0xD3,
+      // version
+      QUIC_VERSION_BYTES,
+      // destination connection ID length
+      0x50,
+      // destination connection ID
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // long header packet length
+      0x1E,
+      // packet number
+      0x12, 0x34, 0x56, 0x79,
+      // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set)
+      0x08 | 0x01 | 0x02 | 0x04,
+      // stream id
+      kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04,
+      // offset
+      kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+      0x32, 0x10, 0x76, 0x54,
+      // data length
+      kVarInt62OneByte + 0x0c,
+      // data
+      'H',  'E',  'L',  'L',
+      'O',  '_',  'W',  'O',
+      'R',  'L',  'D',  '?',
+  };
+  // clang-format on
+
+  QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false);
+  EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+
+  ASSERT_EQ(1u, visitor_.stream_frames_.size());
+  EXPECT_EQ(0u, visitor_.ack_frames_.size());
+
+  // Stream ID should be the last 3 bytes of kStreamId.
+  EXPECT_EQ(0x00FFFFFF & kStreamId, visitor_.stream_frames_[0]->stream_id);
+  EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+  EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset);
+  CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get());
+
+  ASSERT_EQ(visitor_.coalesced_packets_.size(), 1);
+  EXPECT_TRUE(framer_.ProcessPacket(*visitor_.coalesced_packets_[0].get()));
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+
+  ASSERT_EQ(2u, visitor_.stream_frames_.size());
+  EXPECT_EQ(0u, visitor_.ack_frames_.size());
+
+  // Stream ID should be the last 3 bytes of kStreamId.
+  EXPECT_EQ(0x00FFFFFF & kStreamId, visitor_.stream_frames_[1]->stream_id);
+  EXPECT_TRUE(visitor_.stream_frames_[1]->fin);
+  EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[1]->offset);
+  CheckStreamFrameData("HELLO_WORLD?", visitor_.stream_frames_[1].get());
+}
+
+TEST_P(QuicFramerTest, MismatchedCoalescedPacket) {
+  if (!QuicVersionHasLongHeaderLengths(framer_.transport_version())) {
+    return;
+  }
+  // clang-format off
+  unsigned char packet[] = {
+    // first coalesced packet
+      // public flags (long header with packet type ZERO_RTT_PROTECTED and
+      // 4-byte packet number)
+      0xD3,
+      // version
+      QUIC_VERSION_BYTES,
+      // destination connection ID length
+      0x50,
+      // destination connection ID
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // long header packet length
+      0x1E,
+      // packet number
+      0x12, 0x34, 0x56, 0x78,
+      // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set)
+      0x08 | 0x01 | 0x02 | 0x04,
+      // stream id
+      kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04,
+      // offset
+      kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+      0x32, 0x10, 0x76, 0x54,
+      // data length
+      kVarInt62OneByte + 0x0c,
+      // data
+      'h',  'e',  'l',  'l',
+      'o',  ' ',  'w',  'o',
+      'r',  'l',  'd',  '!',
+    // second coalesced packet
+      // public flags (long header with packet type ZERO_RTT_PROTECTED and
+      // 4-byte packet number)
+      0xD3,
+      // version
+      QUIC_VERSION_BYTES,
+      // destination connection ID length
+      0x50,
+      // destination connection ID
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11,
+      // long header packet length
+      0x1E,
+      // packet number
+      0x12, 0x34, 0x56, 0x79,
+      // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set)
+      0x08 | 0x01 | 0x02 | 0x04,
+      // stream id
+      kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04,
+      // offset
+      kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+      0x32, 0x10, 0x76, 0x54,
+      // data length
+      kVarInt62OneByte + 0x0c,
+      // data
+      'H',  'E',  'L',  'L',
+      'O',  '_',  'W',  'O',
+      'R',  'L',  'D',  '?',
+  };
+  // clang-format on
+
+  QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false);
+  EXPECT_QUIC_PEER_BUG(EXPECT_TRUE(framer_.ProcessPacket(encrypted)),
+                       "Server: Received mismatched coalesced header.*");
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+
+  ASSERT_EQ(1u, visitor_.stream_frames_.size());
+  EXPECT_EQ(0u, visitor_.ack_frames_.size());
+
+  // Stream ID should be the last 3 bytes of kStreamId.
+  EXPECT_EQ(0x00FFFFFF & kStreamId, visitor_.stream_frames_[0]->stream_id);
+  EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+  EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset);
+  CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get());
+
+  ASSERT_EQ(visitor_.coalesced_packets_.size(), 0);
+}
+
+TEST_P(QuicFramerTest, InvalidCoalescedPacket) {
+  if (!QuicVersionHasLongHeaderLengths(framer_.transport_version())) {
+    return;
+  }
+  // clang-format off
+  unsigned char packet[] = {
+    // first coalesced packet
+      // public flags (long header with packet type ZERO_RTT_PROTECTED and
+      // 4-byte packet number)
+      0xD3,
+      // version
+      QUIC_VERSION_BYTES,
+      // destination connection ID length
+      0x50,
+      // destination connection ID
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // long header packet length
+      0x1E,
+      // packet number
+      0x12, 0x34, 0x56, 0x78,
+      // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set)
+      0x08 | 0x01 | 0x02 | 0x04,
+      // stream id
+      kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04,
+      // offset
+      kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+      0x32, 0x10, 0x76, 0x54,
+      // data length
+      kVarInt62OneByte + 0x0c,
+      // data
+      'h',  'e',  'l',  'l',
+      'o',  ' ',  'w',  'o',
+      'r',  'l',  'd',  '!',
+    // second coalesced packet
+      // public flags (long header with packet type ZERO_RTT_PROTECTED and
+      // 4-byte packet number)
+      0xD3,
+      // version would be here but we cut off the invalid coalesced header.
+  };
+  // clang-format on
+
+  QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false);
+  EXPECT_QUIC_PEER_BUG(EXPECT_TRUE(framer_.ProcessPacket(encrypted)),
+                       "Server: Failed to parse received coalesced header.*");
+
+  EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+  ASSERT_TRUE(visitor_.header_.get());
+
+  ASSERT_EQ(1u, visitor_.stream_frames_.size());
+  EXPECT_EQ(0u, visitor_.ack_frames_.size());
+
+  // Stream ID should be the last 3 bytes of kStreamId.
+  EXPECT_EQ(0x00FFFFFF & kStreamId, visitor_.stream_frames_[0]->stream_id);
+  EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+  EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset);
+  CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get());
+
+  ASSERT_EQ(visitor_.coalesced_packets_.size(), 0);
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/quic_ietf_framer_test.cc b/quic/core/quic_ietf_framer_test.cc
index d35800a..100fe8d 100644
--- a/quic/core/quic_ietf_framer_test.cc
+++ b/quic/core/quic_ietf_framer_test.cc
@@ -45,9 +45,12 @@
 namespace {
 
 const size_t kNormalPacketBufferSize = 1400;
-// several different stream ids, should be encoded
+// Several different stream ids, should be encoded
 // in 8, 4, 2, and 1 byte, respectively. Last one
 // checks that value==0 works.
+// All stream IDs end in 0x0, so the client/server- initiated
+// and Uni/Bi-directional bits are available to alter, as any
+// given test may wish.
 const QuicIetfStreamId kStreamId8 = UINT64_C(0x3EDCBA9876543210);
 const QuicIetfStreamId kStreamId4 = UINT64_C(0x36543210);
 const QuicIetfStreamId kStreamId2 = UINT64_C(0x3210);
@@ -110,6 +113,8 @@
 
   bool OnPacketHeader(const QuicPacketHeader& header) override { return true; }
 
+  void OnCoalescedPacket(const QuicEncryptedPacket& packet) override {}
+
   bool OnStreamFrame(const QuicStreamFrame& frame) override { return true; }
 
   bool OnCryptoFrame(const QuicCryptoFrame& frame) override { return true; }
@@ -452,6 +457,129 @@
     EXPECT_EQ(receive_frame.byte_offset, transmit_frame.byte_offset);
   }
 
+  void TryMaxStreamsFrame(QuicStreamId stream_id,
+                          bool unidirectional,
+                          bool stream_id_server_initiated) {
+    if (!unidirectional && !stream_id_server_initiated && stream_id == 0) {
+      // For bidirectional, client initiated, streams, 0 is not allowed,
+      // it's used for the crypto stream and is not included in the counting.
+      return;
+    }
+
+    char packet_buffer[kNormalPacketBufferSize];
+    memset(packet_buffer, 0, sizeof(packet_buffer));
+
+    Perspective old_perspective = framer_.perspective();
+    // Set up the writer and transmit QuicMaxStreamIdFrame
+    QuicDataWriter writer(sizeof(packet_buffer), packet_buffer,
+                          NETWORK_BYTE_ORDER);
+    if (stream_id_server_initiated) {
+      stream_id |= 0x01;
+    }
+    if (unidirectional) {
+      stream_id |= 0x02;
+    }
+
+    // Set the perspective of the sender. If the stream id is supposed to
+    // be server-initiated, then the sender of the MAX_STREAMS should be
+    // a client, and vice versa. Do this prior to constructing the frame or
+    // generating the packet, so that any internal dependencies are satisfied.
+    QuicFramerPeer::SetPerspective(&framer_, (stream_id_server_initiated)
+                                                 ? Perspective::IS_CLIENT
+                                                 : Perspective::IS_SERVER);
+    QuicMaxStreamIdFrame transmit_frame(0, stream_id);
+
+    // Add the frame.
+    EXPECT_TRUE(QuicFramerPeer::AppendMaxStreamsFrame(&framer_, transmit_frame,
+                                                      &writer));
+
+    // Check that buffer length is in the expected range
+    EXPECT_LE(1u, writer.length());
+    EXPECT_GE(8u, writer.length());
+
+    // Set the perspective for the receiver.
+    QuicFramerPeer::SetPerspective(&framer_, (stream_id_server_initiated)
+                                                 ? Perspective::IS_SERVER
+                                                 : Perspective::IS_CLIENT);
+
+    // Set up reader and empty receive QuicPaddingFrame.
+    QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
+    QuicMaxStreamIdFrame receive_frame;
+
+    // Deframe it
+    EXPECT_TRUE(QuicFramerPeer::ProcessMaxStreamsFrame(
+        &framer_, &reader, &receive_frame,
+        (unidirectional) ? IETF_MAX_STREAMS_UNIDIRECTIONAL
+                         : IETF_MAX_STREAMS_BIDIRECTIONAL))
+        << " Error: " << framer_.detailed_error();
+
+    // Now check that received and sent data are equivalent
+    EXPECT_EQ(stream_id, receive_frame.max_stream_id);
+    EXPECT_EQ(transmit_frame.max_stream_id, receive_frame.max_stream_id);
+    QuicFramerPeer::SetPerspective(&framer_, old_perspective);
+  }
+
+  void TryStreamsBlockedFrame(QuicStreamId stream_id,
+                              bool unidirectional,
+                              bool stream_id_server_initiated) {
+    if (!unidirectional && !stream_id_server_initiated && stream_id == 0) {
+      // For bidirectional, client initiated, streams, 0 is not allowed,
+      // it's used for the crypto stream and is not included in the counting.
+      return;
+    }
+
+    char packet_buffer[kNormalPacketBufferSize];
+    memset(packet_buffer, 0, sizeof(packet_buffer));
+
+    Perspective old_perspective = framer_.perspective();
+    // Set up the writer and transmit QuicMaxStreamIdFrame
+    QuicDataWriter writer(sizeof(packet_buffer), packet_buffer,
+                          NETWORK_BYTE_ORDER);
+    if (stream_id_server_initiated) {
+      stream_id |= 0x01;
+    }
+    if (unidirectional) {
+      stream_id |= 0x02;
+    }
+
+    // Set the perspective of the sender. If the stream id is supposed to
+    // be server-initiated, then the sender of the MAX_STREAMS should be
+    // a client, and vice versa. Do this prior to constructing the frame or
+    // generating the packet, so that any internal dependencies are satisfied.
+    QuicFramerPeer::SetPerspective(&framer_, (stream_id_server_initiated)
+                                                 ? Perspective::IS_SERVER
+                                                 : Perspective::IS_CLIENT);
+    QuicStreamIdBlockedFrame transmit_frame(0, stream_id);
+
+    // Add the frame.
+    EXPECT_TRUE(QuicFramerPeer::AppendStreamsBlockedFrame(
+        &framer_, transmit_frame, &writer));
+
+    // Check that buffer length is in the expected range
+    EXPECT_LE(1u, writer.length());
+    EXPECT_GE(8u, writer.length());
+
+    // Set the perspective for the receiver.
+    QuicFramerPeer::SetPerspective(&framer_, (stream_id_server_initiated)
+                                                 ? Perspective::IS_CLIENT
+                                                 : Perspective::IS_SERVER);
+
+    // Set up reader and empty receive QuicPaddingFrame.
+    QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
+    QuicStreamIdBlockedFrame receive_frame;
+
+    // Deframe it
+    EXPECT_TRUE(QuicFramerPeer::ProcessStreamsBlockedFrame(
+        &framer_, &reader, &receive_frame,
+        (unidirectional) ? IETF_STREAMS_BLOCKED_UNIDIRECTIONAL
+                         : IETF_STREAMS_BLOCKED_BIDIRECTIONAL));
+
+    // Now check that received and sent data are equivalent
+    EXPECT_EQ(stream_id, receive_frame.stream_id);
+    EXPECT_EQ(transmit_frame.stream_id, receive_frame.stream_id);
+    QuicFramerPeer::SetPerspective(&framer_, old_perspective);
+  }
+
   QuicTime start_;
   QuicFramer framer_;
   test::TestQuicVisitor visitor_;
@@ -1082,7 +1210,8 @@
     // Now check that the received data equals the sent data.
     EXPECT_EQ(transmit_frame.byte_offset, window_size);
     EXPECT_EQ(transmit_frame.byte_offset, receive_frame.byte_offset);
-    EXPECT_EQ(0u, receive_frame.stream_id);
+    EXPECT_EQ(QuicUtils::GetInvalidStreamId(framer_.transport_version()),
+              receive_frame.stream_id);
   }
 }
 
@@ -1128,38 +1257,21 @@
   }
 }
 
-TEST_F(QuicIetfFramerTest, MaxStreamIdFrame) {
-  char packet_buffer[kNormalPacketBufferSize];
+TEST_F(QuicIetfFramerTest, MaxStreamsFrame) {
   QuicIetfStreamId stream_ids[] = {kStreamId4, kStreamId2, kStreamId1,
                                    kStreamId0};
 
   for (QuicIetfStreamId stream_id : stream_ids) {
-    memset(packet_buffer, 0, sizeof(packet_buffer));
-
-    // Set up the writer and transmit QuicMaxStreamIdFrame
-    QuicDataWriter writer(sizeof(packet_buffer), packet_buffer,
-                          NETWORK_BYTE_ORDER);
-    QuicMaxStreamIdFrame transmit_frame(0, stream_id);
-
-    // Add the frame.
-    EXPECT_TRUE(QuicFramerPeer::AppendMaxStreamIdFrame(&framer_, transmit_frame,
-                                                       &writer));
-
-    // Check that buffer length is in the expected range
-    EXPECT_LE(1u, writer.length());
-    EXPECT_GE(8u, writer.length());
-
-    // Set up reader and empty receive QuicPaddingFrame.
-    QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
-    QuicMaxStreamIdFrame receive_frame;
-
-    // Deframe it
-    EXPECT_TRUE(QuicFramerPeer::ProcessMaxStreamIdFrame(&framer_, &reader,
-                                                        &receive_frame));
-
-    // Now check that received and sent data are equivalent
-    EXPECT_EQ(stream_id, receive_frame.max_stream_id);
-    EXPECT_EQ(transmit_frame.max_stream_id, receive_frame.max_stream_id);
+    // Cover all four combinations of uni/bi-directional and
+    // server-/client- initiation.
+    TryMaxStreamsFrame(stream_id, /*unidirectional=*/true,
+                       /*stream_id_server_initiated=*/true);
+    TryMaxStreamsFrame(stream_id, /*unidirectional=*/true,
+                       /*stream_id_server_initiated=*/false);
+    TryMaxStreamsFrame(stream_id, /*unidirectional=*/false,
+                       /*stream_id_server_initiated=*/true);
+    TryMaxStreamsFrame(stream_id, /*unidirectional=*/false,
+                       /*stream_id_server_initiated=*/false);
   }
 }
 
@@ -1174,7 +1286,8 @@
     // Set up the writer and transmit QuicBlockedFrame
     QuicDataWriter writer(sizeof(packet_buffer), packet_buffer,
                           NETWORK_BYTE_ORDER);
-    QuicBlockedFrame transmit_frame(0, 0, offset);
+    QuicBlockedFrame transmit_frame(
+        0, QuicUtils::GetInvalidStreamId(framer_.transport_version()), offset);
 
     // Add the frame.
     EXPECT_TRUE(QuicFramerPeer::AppendIetfBlockedFrame(&framer_, transmit_frame,
@@ -1193,7 +1306,8 @@
                                                         &receive_frame));
 
     // Check that received and sent data are equivalent
-    EXPECT_EQ(0u, receive_frame.stream_id);
+    EXPECT_EQ(QuicUtils::GetInvalidStreamId(framer_.transport_version()),
+              receive_frame.stream_id);
     EXPECT_EQ(offset, receive_frame.offset);
     EXPECT_EQ(transmit_frame.offset, receive_frame.offset);
   }
@@ -1241,38 +1355,23 @@
   }
 }
 
-TEST_F(QuicIetfFramerTest, StreamIdBlockedFrame) {
-  char packet_buffer[kNormalPacketBufferSize];
+TEST_F(QuicIetfFramerTest, StreamsBlockedFrame) {
   QuicIetfStreamId stream_ids[] = {kStreamId4, kStreamId2, kStreamId1,
                                    kStreamId0};
 
   for (QuicIetfStreamId stream_id : stream_ids) {
-    memset(packet_buffer, 0, sizeof(packet_buffer));
-
-    // Set up the writer and transmit QuicStreamIdBlockedFrame
-    QuicDataWriter writer(sizeof(packet_buffer), packet_buffer,
-                          NETWORK_BYTE_ORDER);
-    QuicStreamIdBlockedFrame transmit_frame(0, stream_id);
-
-    // Add the frame.
-    EXPECT_TRUE(QuicFramerPeer::AppendStreamIdBlockedFrame(
-        &framer_, transmit_frame, &writer));
-
-    // Check that buffer length is in the expected range
-    EXPECT_LE(1u, writer.length());
-    EXPECT_GE(8u, writer.length());
-
-    // Set up reader and empty receive QuicPaddingFrame.
-    QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
-    QuicStreamIdBlockedFrame receive_frame;
-
-    // Deframe it
-    EXPECT_TRUE(QuicFramerPeer::ProcessStreamIdBlockedFrame(&framer_, &reader,
-                                                            &receive_frame));
-
-    // Now check that received == sent
-    EXPECT_EQ(stream_id, receive_frame.stream_id);
-    EXPECT_EQ(transmit_frame.stream_id, receive_frame.stream_id);
+    TryStreamsBlockedFrame(stream_id,
+                           /*unidirectional=*/false,
+                           /*stream_id_server_initiated=*/false);
+    TryStreamsBlockedFrame(stream_id,
+                           /*unidirectional=*/false,
+                           /*stream_id_server_initiated=*/true);
+    TryStreamsBlockedFrame(stream_id,
+                           /*unidirectional=*/true,
+                           /*stream_id_server_initiated=*/false);
+    TryStreamsBlockedFrame(stream_id,
+                           /*unidirectional=*/true,
+                           /*stream_id_server_initiated=*/true);
   }
 }
 
diff --git a/quic/core/quic_interval.h b/quic/core/quic_interval.h
new file mode 100644
index 0000000..d5bf171
--- /dev/null
+++ b/quic/core/quic_interval.h
@@ -0,0 +1,379 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_INTERVAL_H_
+#define QUICHE_QUIC_CORE_QUIC_INTERVAL_H_
+
+// An QuicInterval<T> is a data structure used to represent a contiguous,
+// mutable range over an ordered type T. Supported operations include testing a
+// value to see whether it is included in the QuicInterval, comparing two
+// QuicIntervals, and performing their union, intersection, and difference. For
+// the purposes of this library, an "ordered type" is any type that induces a
+// total order on its values via its less-than operator (operator<()). Examples
+// of such types are basic arithmetic types like int and double as well as class
+// types like string.
+//
+// An QuicInterval<T> is represented using the usual C++ STL convention, namely
+// as the half-open QuicInterval [min, max). A point p is considered to be
+// contained in the QuicInterval iff p >= min && p < max. One consequence of
+// this definition is that for any non-empty QuicInterval, min is contained in
+// the QuicInterval but max is not. There is no canonical representation for the
+// empty QuicInterval; rather, any QuicInterval where max <= min is regarded as
+// empty. As a consequence, two empty QuicIntervals will still compare as equal
+// despite possibly having different underlying min() or max() values. Also
+// beware of the terminology used here: the library uses the terms "min" and
+// "max" rather than "begin" and "end" as is conventional for the STL.
+//
+// T is required to be default- and copy-constructable, to have an assignment
+// operator, and the full complement of comparison operators (<, <=, ==, !=, >=,
+// >).  A difference operator (operator-()) is required if
+// QuicInterval<T>::Length is used.
+//
+// QuicInterval supports operator==. Two QuicIntervals are considered equal if
+// either they are both empty or if their corresponding min and max fields
+// compare equal. QuicInterval also provides an operator<. Unfortunately,
+// operator< is currently buggy because its behavior is inconsistent with
+// operator==: two empty ranges with different representations may be regarded
+// as equal by operator== but regarded as different by operator<. Bug 9240050
+// has been created to address this.
+//
+//
+// Examples:
+//   QuicInterval<int> r1(0, 100);  // The QuicInterval [0, 100).
+//   EXPECT_TRUE(r1.Contains(0));
+//   EXPECT_TRUE(r1.Contains(50));
+//   EXPECT_FALSE(r1.Contains(100));  // 100 is just outside the QuicInterval.
+//
+//   QuicInterval<int> r2(50, 150);  // The QuicInterval [50, 150).
+//   EXPECT_TRUE(r1.Intersects(r2));
+//   EXPECT_FALSE(r1.Contains(r2));
+//   EXPECT_TRUE(r1.IntersectWith(r2));  // Mutates r1.
+//   EXPECT_EQ(QuicInterval<int>(50, 100), r1);  // r1 is now [50, 100).
+//
+//   QuicInterval<int> r3(1000, 2000);  // The QuicInterval [1000, 2000).
+//   EXPECT_TRUE(r1.IntersectWith(r3));  // Mutates r1.
+//   EXPECT_TRUE(r1.Empty());  // Now r1 is empty.
+//   EXPECT_FALSE(r1.Contains(r1.min()));  // e.g. doesn't contain its own min.
+
+#include <stddef.h>
+#include <algorithm>
+#include <ostream>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+namespace quic {
+
+template <typename T>
+class QuicInterval {
+ private:
+  // Type trait for deriving the return type for QuicInterval::Length.  If
+  // operator-() is not defined for T, then the return type is void.  This makes
+  // the signature for Length compile so that the class can be used for such T,
+  // but code that calls Length would still generate a compilation error.
+  template <typename U>
+  class DiffTypeOrVoid {
+   private:
+    template <typename V>
+    static auto f(const V* v) -> decltype(*v - *v);
+    template <typename V>
+    static void f(...);
+
+   public:
+    using type = typename std::decay<decltype(f<U>(nullptr))>::type;
+  };
+
+ public:
+  // Construct an QuicInterval representing an empty QuicInterval.
+  QuicInterval() : min_(), max_() {}
+
+  // Construct an QuicInterval representing the QuicInterval [min, max). If min
+  // < max, the constructed object will represent the non-empty QuicInterval
+  // containing all values from min up to (but not including) max. On the other
+  // hand, if min >= max, the constructed object will represent the empty
+  // QuicInterval.
+  QuicInterval(const T& min, const T& max) : min_(min), max_(max) {}
+
+  template <typename U1,
+            typename U2,
+            typename = typename std::enable_if<
+                std::is_convertible<U1, T>::value &&
+                std::is_convertible<U2, T>::value>::type>
+  QuicInterval(U1&& min, U2&& max)
+      : min_(std::forward<U1>(min)), max_(std::forward<U2>(max)) {}
+
+  const T& min() const { return min_; }
+  const T& max() const { return max_; }
+  void SetMin(const T& t) { min_ = t; }
+  void SetMax(const T& t) { max_ = t; }
+
+  void Set(const T& min, const T& max) {
+    SetMin(min);
+    SetMax(max);
+  }
+
+  void Clear() { *this = {}; }
+
+  bool Empty() const { return min() >= max(); }
+
+  // Returns the length of this QuicInterval. The value returned is zero if
+  // Empty() is true; otherwise the value returned is max() - min().
+  typename DiffTypeOrVoid<T>::type Length() const {
+    return (Empty() ? min() : max()) - min();
+  }
+
+  // Returns true iff t >= min() && t < max().
+  bool Contains(const T& t) const { return min() <= t && max() > t; }
+
+  // Returns true iff *this and i are non-empty, and *this includes i. "*this
+  // includes i" means that for all t, if i.Contains(t) then this->Contains(t).
+  // Note the unintuitive consequence of this definition: this method always
+  // returns false when i is the empty QuicInterval.
+  bool Contains(const QuicInterval& i) const {
+    return !Empty() && !i.Empty() && min() <= i.min() && max() >= i.max();
+  }
+
+  // Returns true iff there exists some point t for which this->Contains(t) &&
+  // i.Contains(t) evaluates to true, i.e. if the intersection is non-empty.
+  bool Intersects(const QuicInterval& i) const {
+    return !Empty() && !i.Empty() && min() < i.max() && max() > i.min();
+  }
+
+  // Returns true iff there exists some point t for which this->Contains(t) &&
+  // i.Contains(t) evaluates to true, i.e. if the intersection is non-empty.
+  // Furthermore, if the intersection is non-empty and the out pointer is not
+  // null, this method stores the calculated intersection in *out.
+  bool Intersects(const QuicInterval& i, QuicInterval* out) const;
+
+  // Sets *this to be the intersection of itself with i. Returns true iff
+  // *this was modified.
+  bool IntersectWith(const QuicInterval& i);
+
+  // Calculates the smallest QuicInterval containing both *this i, and updates
+  // *this to represent that QuicInterval, and returns true iff *this was
+  // modified.
+  bool SpanningUnion(const QuicInterval& i);
+
+  // Determines the difference between two QuicIntervals by finding all points
+  // that are contained in *this but not in i, coalesces those points into the
+  // largest possible contiguous QuicIntervals, and appends those QuicIntervals
+  // to the *difference vector. Intuitively this can be thought of as "erasing"
+  // i from *this. This will either completely erase *this (leaving nothing
+  // behind), partially erase some of *this from the left or right side (leaving
+  // some residual behind), or erase a hole in the middle of *this (leaving
+  // behind an QuicInterval on either side). Therefore, 0, 1, or 2 QuicIntervals
+  // will be appended to *difference. The method returns true iff the
+  // intersection of *this and i is non-empty. The caller owns the vector and
+  // the QuicInterval* pointers inside it. The difference vector is required to
+  // be non-null.
+  bool Difference(const QuicInterval& i,
+                  std::vector<QuicInterval*>* difference) const;
+
+  // Determines the difference between two QuicIntervals as in
+  // Difference(QuicInterval&, vector*), but stores the results directly in out
+  // parameters rather than dynamically allocating an QuicInterval* and
+  // appending it to a vector. If two results are generated, the one with the
+  // smaller value of min() will be stored in *lo and the other in *hi.
+  // Otherwise (if fewer than two results are generated), unused arguments will
+  // be set to the empty QuicInterval (it is possible that *lo will be empty and
+  // *hi non-empty). The method returns true iff the intersection of *this and i
+  // is non-empty.
+  bool Difference(const QuicInterval& i,
+                  QuicInterval* lo,
+                  QuicInterval* hi) const;
+
+  friend bool operator==(const QuicInterval& a, const QuicInterval& b) {
+    bool ae = a.Empty();
+    bool be = b.Empty();
+    if (ae && be)
+      return true;  // All empties are equal.
+    if (ae != be)
+      return false;  // Empty cannot equal nonempty.
+    return a.min() == b.min() && a.max() == b.max();
+  }
+
+  friend bool operator!=(const QuicInterval& a, const QuicInterval& b) {
+    return !(a == b);
+  }
+
+  // Defines a comparator which can be used to induce an order on QuicIntervals,
+  // so that, for example, they can be stored in an ordered container such as
+  // std::set. The ordering is arbitrary, but does provide the guarantee that,
+  // for non-empty QuicIntervals X and Y, if X contains Y, then X <= Y.
+  // TODO(kosak): The current implementation of this comparator has a problem
+  // because the ordering it induces is inconsistent with that of Equals(). In
+  // particular, this comparator does not properly consider all empty
+  // QuicIntervals equivalent. Bug 9240050 has been created to track this.
+  friend bool operator<(const QuicInterval& a, const QuicInterval& b) {
+    return a.min() < b.min() || (!(b.min() < a.min()) && b.max() < a.max());
+  }
+
+ private:
+  T min_;  // Inclusive lower bound.
+  T max_;  // Exclusive upper bound.
+};
+
+// Constructs an QuicInterval by deducing the types from the function arguments.
+template <typename T>
+QuicInterval<T> MakeQuicInterval(T&& lhs, T&& rhs) {
+  return QuicInterval<T>(std::forward<T>(lhs), std::forward<T>(rhs));
+}
+
+// Note: ideally we'd use
+//   decltype(out << "[" << i.min() << ", " << i.max() << ")")
+// as return type of the function, but as of July 2017 this triggers g++
+// "sorry, unimplemented: string literal in function template signature" error.
+template <typename T>
+auto operator<<(std::ostream& out, const QuicInterval<T>& i)
+    -> decltype(out << i.min()) {
+  return out << "[" << i.min() << ", " << i.max() << ")";
+}
+
+//==============================================================================
+// Implementation details: Clients can stop reading here.
+
+template <typename T>
+bool QuicInterval<T>::Intersects(const QuicInterval& i,
+                                 QuicInterval* out) const {
+  if (!Intersects(i))
+    return false;
+  if (out != nullptr) {
+    *out = QuicInterval(std::max(min(), i.min()), std::min(max(), i.max()));
+  }
+  return true;
+}
+
+template <typename T>
+bool QuicInterval<T>::IntersectWith(const QuicInterval& i) {
+  if (Empty())
+    return false;
+  bool modified = false;
+  if (i.min() > min()) {
+    SetMin(i.min());
+    modified = true;
+  }
+  if (i.max() < max()) {
+    SetMax(i.max());
+    modified = true;
+  }
+  return modified;
+}
+
+template <typename T>
+bool QuicInterval<T>::SpanningUnion(const QuicInterval& i) {
+  if (i.Empty())
+    return false;
+  if (Empty()) {
+    *this = i;
+    return true;
+  }
+  bool modified = false;
+  if (i.min() < min()) {
+    SetMin(i.min());
+    modified = true;
+  }
+  if (i.max() > max()) {
+    SetMax(i.max());
+    modified = true;
+  }
+  return modified;
+}
+
+template <typename T>
+bool QuicInterval<T>::Difference(const QuicInterval& i,
+                                 std::vector<QuicInterval*>* difference) const {
+  if (Empty()) {
+    // <empty> - <i> = <empty>
+    return false;
+  }
+  if (i.Empty()) {
+    // <this> - <empty> = <this>
+    difference->push_back(new QuicInterval(*this));
+    return false;
+  }
+  if (min() < i.max() && min() >= i.min() && max() > i.max()) {
+    //            [------ this ------)
+    // [------ i ------)
+    //                 [-- result ---)
+    difference->push_back(new QuicInterval(i.max(), max()));
+    return true;
+  }
+  if (max() > i.min() && max() <= i.max() && min() < i.min()) {
+    // [------ this ------)
+    //            [------ i ------)
+    // [- result -)
+    difference->push_back(new QuicInterval(min(), i.min()));
+    return true;
+  }
+  if (min() < i.min() && max() > i.max()) {
+    // [------- this --------)
+    //      [---- i ----)
+    // [ R1 )           [ R2 )
+    // There are two results: R1 and R2.
+    difference->push_back(new QuicInterval(min(), i.min()));
+    difference->push_back(new QuicInterval(i.max(), max()));
+    return true;
+  }
+  if (min() >= i.min() && max() <= i.max()) {
+    //   [--- this ---)
+    // [------ i --------)
+    // Intersection is <this>, so difference yields the empty QuicInterval.
+    // Nothing is appended to *difference.
+    return true;
+  }
+  // No intersection. Append <this>.
+  difference->push_back(new QuicInterval(*this));
+  return false;
+}
+
+template <typename T>
+bool QuicInterval<T>::Difference(const QuicInterval& i,
+                                 QuicInterval* lo,
+                                 QuicInterval* hi) const {
+  // Initialize *lo and *hi to empty
+  *lo = {};
+  *hi = {};
+  if (Empty())
+    return false;
+  if (i.Empty()) {
+    *lo = *this;
+    return false;
+  }
+  if (min() < i.max() && min() >= i.min() && max() > i.max()) {
+    //            [------ this ------)
+    // [------ i ------)
+    //                 [-- result ---)
+    *hi = QuicInterval(i.max(), max());
+    return true;
+  }
+  if (max() > i.min() && max() <= i.max() && min() < i.min()) {
+    // [------ this ------)
+    //            [------ i ------)
+    // [- result -)
+    *lo = QuicInterval(min(), i.min());
+    return true;
+  }
+  if (min() < i.min() && max() > i.max()) {
+    // [------- this --------)
+    //      [---- i ----)
+    // [ R1 )           [ R2 )
+    // There are two results: R1 and R2.
+    *lo = QuicInterval(min(), i.min());
+    *hi = QuicInterval(i.max(), max());
+    return true;
+  }
+  if (min() >= i.min() && max() <= i.max()) {
+    //   [--- this ---)
+    // [------ i --------)
+    // Intersection is <this>, so difference yields the empty QuicInterval.
+    return true;
+  }
+  *lo = *this;  // No intersection.
+  return false;
+}
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_CORE_QUIC_INTERVAL_H_
diff --git a/quic/core/quic_interval_set.h b/quic/core/quic_interval_set.h
new file mode 100644
index 0000000..937412a
--- /dev/null
+++ b/quic/core/quic_interval_set.h
@@ -0,0 +1,883 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_INTERVAL_SET_H_
+#define QUICHE_QUIC_CORE_QUIC_INTERVAL_SET_H_
+
+// QuicIntervalSet<T> is a data structure used to represent a sorted set of
+// non-empty, non-adjacent, and mutually disjoint intervals. Mutations to an
+// interval set preserve these properties, altering the set as needed. For
+// example, adding [2, 3) to a set containing only [1, 2) would result in the
+// set containing the single interval [1, 3).
+//
+// Supported operations include testing whether an Interval is contained in the
+// QuicIntervalSet, comparing two QuicIntervalSets, and performing
+// QuicIntervalSet union, intersection, and difference.
+//
+// QuicIntervalSet maintains the minimum number of entries needed to represent
+// the set of underlying intervals. When the QuicIntervalSet is modified (e.g.
+// due to an Add operation), other interval entries may be coalesced, removed,
+// or otherwise modified in order to maintain this invariant. The intervals are
+// maintained in sorted order, by ascending min() value.
+//
+// The reader is cautioned to beware of the terminology used here: this library
+// uses the terms "min" and "max" rather than "begin" and "end" as is
+// conventional for the STL. The terminology [min, max) refers to the half-open
+// interval which (if the interval is not empty) contains min but does not
+// contain max. An interval is considered empty if min >= max.
+//
+// T is required to be default- and copy-constructible, to have an assignment
+// operator, a difference operator (operator-()), and the full complement of
+// comparison operators (<, <=, ==, !=, >=, >). These requirements are inherited
+// from value_type.
+//
+// QuicIntervalSet has constant-time move operations.
+//
+//
+// Examples:
+//   QuicIntervalSet<int> intervals;
+//   intervals.Add(Interval<int>(10, 20));
+//   intervals.Add(Interval<int>(30, 40));
+//   // intervals contains [10,20) and [30,40).
+//   intervals.Add(Interval<int>(15, 35));
+//   // intervals has been coalesced. It now contains the single range [10,40).
+//   EXPECT_EQ(1, intervals.Size());
+//   EXPECT_TRUE(intervals.Contains(Interval<int>(10, 40)));
+//
+//   intervals.Difference(Interval<int>(10, 20));
+//   // intervals should now contain the single range [20, 40).
+//   EXPECT_EQ(1, intervals.Size());
+//   EXPECT_TRUE(intervals.Contains(Interval<int>(20, 40)));
+
+#include <stddef.h>
+#include <algorithm>
+#include <initializer_list>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+template <typename T>
+class QuicIntervalSet {
+ public:
+  typedef QuicInterval<T> value_type;
+
+ private:
+  struct IntervalLess {
+    bool operator()(const value_type& a, const value_type& b) const;
+  };
+  typedef std::set<value_type, IntervalLess> Set;
+
+ public:
+  typedef typename Set::const_iterator const_iterator;
+  typedef typename Set::const_reverse_iterator const_reverse_iterator;
+
+  // Instantiates an empty QuicIntervalSet.
+  QuicIntervalSet() {}
+
+  // Instantiates an QuicIntervalSet containing exactly one initial half-open
+  // interval [min, max), unless the given interval is empty, in which case the
+  // QuicIntervalSet will be empty.
+  explicit QuicIntervalSet(const value_type& interval) { Add(interval); }
+
+  // Instantiates an QuicIntervalSet containing the half-open interval [min,
+  // max).
+  QuicIntervalSet(const T& min, const T& max) { Add(min, max); }
+
+  QuicIntervalSet(std::initializer_list<value_type> il) { assign(il); }
+
+  // Clears this QuicIntervalSet.
+  void Clear() { intervals_.clear(); }
+
+  // Returns the number of disjoint intervals contained in this QuicIntervalSet.
+  size_t Size() const { return intervals_.size(); }
+
+  // Returns the smallest interval that contains all intervals in this
+  // QuicIntervalSet, or the empty interval if the set is empty.
+  value_type SpanningInterval() const;
+
+  // Adds "interval" to this QuicIntervalSet. Adding the empty interval has no
+  // effect.
+  void Add(const value_type& interval);
+
+  // Adds the interval [min, max) to this QuicIntervalSet. Adding the empty
+  // interval has no effect.
+  void Add(const T& min, const T& max) { Add(value_type(min, max)); }
+
+  // DEPRECATED(kosak). Use Union() instead. This method merges all of the
+  // values contained in "other" into this QuicIntervalSet.
+  void Add(const QuicIntervalSet& other);
+
+  // Returns true if this QuicIntervalSet is empty.
+  bool Empty() const { return intervals_.empty(); }
+
+  // Returns true if any interval in this QuicIntervalSet contains the indicated
+  // value.
+  bool Contains(const T& value) const;
+
+  // Returns true if there is some interval in this QuicIntervalSet that wholly
+  // contains the given interval. An interval O "wholly contains" a non-empty
+  // interval I if O.Contains(p) is true for every p in I. This is the same
+  // definition used by value_type::Contains(). This method returns false on
+  // the empty interval, due to a (perhaps unintuitive) convention inherited
+  // from value_type.
+  // Example:
+  //   Assume an QuicIntervalSet containing the entries { [10,20), [30,40) }.
+  //   Contains(Interval(15, 16)) returns true, because [10,20) contains
+  //   [15,16). However, Contains(Interval(15, 35)) returns false.
+  bool Contains(const value_type& interval) const;
+
+  // Returns true if for each interval in "other", there is some (possibly
+  // different) interval in this QuicIntervalSet which wholly contains it. See
+  // Contains(const value_type& interval) for the meaning of "wholly contains".
+  // Perhaps unintuitively, this method returns false if "other" is the empty
+  // set. The algorithmic complexity of this method is O(other.Size() *
+  // log(this->Size())). The method could be rewritten to run in O(other.Size()
+  // + this->Size()), and this alternative could be implemented as a free
+  // function using the public API.
+  bool Contains(const QuicIntervalSet<T>& other) const;
+
+  // Returns true if there is some interval in this QuicIntervalSet that wholly
+  // contains the interval [min, max). See Contains(const value_type&).
+  bool Contains(const T& min, const T& max) const {
+    return Contains(value_type(min, max));
+  }
+
+  // Returns true if for some interval in "other", there is some interval in
+  // this QuicIntervalSet that intersects with it. See value_type::Intersects()
+  // for the definition of interval intersection.
+  bool Intersects(const QuicIntervalSet& other) const;
+
+  // Returns an iterator to the value_type in the QuicIntervalSet that contains
+  // the given value. In other words, returns an iterator to the unique interval
+  // [min, max) in the QuicIntervalSet that has the property min <= value < max.
+  // If there is no such interval, this method returns end().
+  const_iterator Find(const T& value) const;
+
+  // Returns an iterator to the value_type in the QuicIntervalSet that wholly
+  // contains the given interval. In other words, returns an iterator to the
+  // unique interval outer in the QuicIntervalSet that has the property that
+  // outer.Contains(interval). If there is no such interval, or if interval is
+  // empty, returns end().
+  const_iterator Find(const value_type& interval) const;
+
+  // Returns an iterator to the value_type in the QuicIntervalSet that wholly
+  // contains [min, max). In other words, returns an iterator to the unique
+  // interval outer in the QuicIntervalSet that has the property that
+  // outer.Contains(Interval<T>(min, max)). If there is no such interval, or if
+  // interval is empty, returns end().
+  const_iterator Find(const T& min, const T& max) const {
+    return Find(value_type(min, max));
+  }
+
+  // Returns an iterator pointing to the first value_type which contains or
+  // goes after the given value.
+  //
+  // Example:
+  //   [10, 20)  [30, 40)
+  //   ^                    LowerBound(10)
+  //   ^                    LowerBound(15)
+  //             ^          LowerBound(20)
+  //             ^          LowerBound(25)
+  const_iterator LowerBound(const T& value) const;
+
+  // Returns an iterator pointing to the first value_type which goes after
+  // the given value.
+  //
+  // Example:
+  //   [10, 20)  [30, 40)
+  //             ^          UpperBound(10)
+  //             ^          UpperBound(15)
+  //             ^          UpperBound(20)
+  //             ^          UpperBound(25)
+  const_iterator UpperBound(const T& value) const;
+
+  // Returns true if every value within the passed interval is not Contained
+  // within the QuicIntervalSet.
+  // Note that empty intervals are always considered disjoint from the
+  // QuicIntervalSet (even though the QuicIntervalSet doesn't `Contain` them).
+  bool IsDisjoint(const value_type& interval) const;
+
+  // Merges all the values contained in "other" into this QuicIntervalSet.
+  void Union(const QuicIntervalSet& other);
+
+  // Modifies this QuicIntervalSet so that it contains only those values that
+  // are currently present both in *this and in the QuicIntervalSet "other".
+  void Intersection(const QuicIntervalSet& other);
+
+  // Mutates this QuicIntervalSet so that it contains only those values that are
+  // currently in *this but not in "interval".
+  void Difference(const value_type& interval);
+
+  // Mutates this QuicIntervalSet so that it contains only those values that are
+  // currently in *this but not in the interval [min, max).
+  void Difference(const T& min, const T& max);
+
+  // Mutates this QuicIntervalSet so that it contains only those values that are
+  // currently in *this but not in the QuicIntervalSet "other".
+  void Difference(const QuicIntervalSet& other);
+
+  // Mutates this QuicIntervalSet so that it contains only those values that are
+  // in [min, max) but not currently in *this.
+  void Complement(const T& min, const T& max);
+
+  // QuicIntervalSet's begin() iterator. The invariants of QuicIntervalSet
+  // guarantee that for each entry e in the set, e.min() < e.max() (because the
+  // entries are non-empty) and for each entry f that appears later in the set,
+  // e.max() < f.min() (because the entries are ordered, pairwise-disjoint, and
+  // non-adjacent). Modifications to this QuicIntervalSet invalidate these
+  // iterators.
+  const_iterator begin() const { return intervals_.begin(); }
+
+  // QuicIntervalSet's end() iterator.
+  const_iterator end() const { return intervals_.end(); }
+
+  // QuicIntervalSet's rbegin() and rend() iterators. Iterator invalidation
+  // semantics are the same as those for begin() / end().
+  const_reverse_iterator rbegin() const { return intervals_.rbegin(); }
+
+  const_reverse_iterator rend() const { return intervals_.rend(); }
+
+  template <typename Iter>
+  void assign(Iter first, Iter last) {
+    Clear();
+    for (; first != last; ++first)
+      Add(*first);
+  }
+
+  void assign(std::initializer_list<value_type> il) {
+    assign(il.begin(), il.end());
+  }
+
+  // Returns a human-readable representation of this set. This will typically be
+  // (though is not guaranteed to be) of the form
+  //   "[a1, b1) [a2, b2) ... [an, bn)"
+  // where the intervals are in the same order as given by traversal from
+  // begin() to end(). This representation is intended for human consumption;
+  // computer programs should not rely on the output being in exactly this form.
+  string ToString() const;
+
+  QuicIntervalSet& operator=(std::initializer_list<value_type> il) {
+    assign(il.begin(), il.end());
+    return *this;
+  }
+
+  // Swap this QuicIntervalSet with *other. This is a constant-time operation.
+  void Swap(QuicIntervalSet<T>* other) { intervals_.swap(other->intervals_); }
+
+  friend bool operator==(const QuicIntervalSet& a, const QuicIntervalSet& b) {
+    return a.Size() == b.Size() &&
+           std::equal(a.begin(), a.end(), b.begin(), NonemptyIntervalEq());
+  }
+
+  friend bool operator!=(const QuicIntervalSet& a, const QuicIntervalSet& b) {
+    return !(a == b);
+  }
+
+ private:
+  // Simple member-wise equality, since all intervals are non-empty.
+  struct NonemptyIntervalEq {
+    bool operator()(const value_type& a, const value_type& b) const {
+      return a.min() == b.min() && a.max() == b.max();
+    }
+  };
+
+  // Removes overlapping ranges and coalesces adjacent intervals as needed.
+  void Compact(const typename Set::iterator& begin,
+               const typename Set::iterator& end);
+
+  // Returns true if this set is valid (i.e. all intervals in it are non-empty,
+  // non-adjacent, and mutually disjoint). Currently this is used as an
+  // integrity check by the Intersection() and Difference() methods, but is only
+  // invoked for debug builds (via DCHECK).
+  bool Valid() const;
+
+  // Finds the first interval that potentially intersects 'other'.
+  const_iterator FindIntersectionCandidate(const QuicIntervalSet& other) const;
+
+  // Finds the first interval that potentially intersects 'interval'.
+  const_iterator FindIntersectionCandidate(const value_type& interval) const;
+
+  // Helper for Intersection() and Difference(): Finds the next pair of
+  // intervals from 'x' and 'y' that intersect. 'mine' is an iterator
+  // over x->intervals_. 'theirs' is an iterator over y.intervals_. 'mine'
+  // and 'theirs' are advanced until an intersecting pair is found.
+  // Non-intersecting intervals (aka "holes") from x->intervals_ can be
+  // optionally erased by "on_hole".
+  template <typename X, typename Func>
+  static bool FindNextIntersectingPairImpl(X* x,
+                                           const QuicIntervalSet& y,
+                                           const_iterator* mine,
+                                           const_iterator* theirs,
+                                           Func on_hole);
+
+  // The variant of the above method that doesn't mutate this QuicIntervalSet.
+  bool FindNextIntersectingPair(const QuicIntervalSet& other,
+                                const_iterator* mine,
+                                const_iterator* theirs) const {
+    return FindNextIntersectingPairImpl(
+        this, other, mine, theirs,
+        [](const QuicIntervalSet*, const_iterator, const_iterator) {});
+  }
+
+  // The variant of the above method that mutates this QuicIntervalSet by
+  // erasing holes.
+  bool FindNextIntersectingPairAndEraseHoles(const QuicIntervalSet& other,
+                                             const_iterator* mine,
+                                             const_iterator* theirs) {
+    return FindNextIntersectingPairImpl(
+        this, other, mine, theirs,
+        [](QuicIntervalSet* x, const_iterator from, const_iterator to) {
+          x->intervals_.erase(from, to);
+        });
+  }
+
+  // The representation for the intervals. The intervals in this set are
+  // non-empty, pairwise-disjoint, non-adjacent and ordered in ascending order
+  // by min().
+  Set intervals_;
+};
+
+template <typename T>
+auto operator<<(std::ostream& out, const QuicIntervalSet<T>& seq)
+    -> decltype(out << *seq.begin()) {
+  out << "{";
+  for (const auto& interval : seq) {
+    out << " " << interval;
+  }
+  out << " }";
+
+  return out;
+}
+
+template <typename T>
+void swap(QuicIntervalSet<T>& x, QuicIntervalSet<T>& y);
+
+//==============================================================================
+// Implementation details: Clients can stop reading here.
+
+template <typename T>
+typename QuicIntervalSet<T>::value_type QuicIntervalSet<T>::SpanningInterval()
+    const {
+  value_type result;
+  if (!intervals_.empty()) {
+    result.SetMin(intervals_.begin()->min());
+    result.SetMax(intervals_.rbegin()->max());
+  }
+  return result;
+}
+
+template <typename T>
+void QuicIntervalSet<T>::Add(const value_type& interval) {
+  if (interval.Empty())
+    return;
+  std::pair<typename Set::iterator, bool> ins = intervals_.insert(interval);
+  if (!ins.second) {
+    // This interval already exists.
+    return;
+  }
+  // Determine the minimal range that will have to be compacted.  We know that
+  // the QuicIntervalSet was valid before the addition of the interval, so only
+  // need to start with the interval itself (although Compact takes an open
+  // range so begin needs to be the interval to the left).  We don't know how
+  // many ranges this interval may cover, so we need to find the appropriate
+  // interval to end with on the right.
+  typename Set::iterator begin = ins.first;
+  if (begin != intervals_.begin())
+    --begin;
+  const value_type target_end(interval.max(), interval.max());
+  const typename Set::iterator end = intervals_.upper_bound(target_end);
+  Compact(begin, end);
+}
+
+template <typename T>
+void QuicIntervalSet<T>::Add(const QuicIntervalSet& other) {
+  for (const_iterator it = other.begin(); it != other.end(); ++it) {
+    Add(*it);
+  }
+}
+
+template <typename T>
+bool QuicIntervalSet<T>::Contains(const T& value) const {
+  value_type tmp(value, value);
+  // Find the first interval with min() > value, then move back one step
+  const_iterator it = intervals_.upper_bound(tmp);
+  if (it == intervals_.begin())
+    return false;
+  --it;
+  return it->Contains(value);
+}
+
+template <typename T>
+bool QuicIntervalSet<T>::Contains(const value_type& interval) const {
+  // Find the first interval with min() > value, then move back one step.
+  const_iterator it = intervals_.upper_bound(interval);
+  if (it == intervals_.begin())
+    return false;
+  --it;
+  return it->Contains(interval);
+}
+
+template <typename T>
+bool QuicIntervalSet<T>::Contains(const QuicIntervalSet<T>& other) const {
+  if (!SpanningInterval().Contains(other.SpanningInterval())) {
+    return false;
+  }
+
+  for (const_iterator i = other.begin(); i != other.end(); ++i) {
+    // If we don't contain the interval, can return false now.
+    if (!Contains(*i)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+// This method finds the interval that Contains() "value", if such an interval
+// exists in the QuicIntervalSet. The way this is done is to locate the
+// "candidate interval", the only interval that could *possibly* contain value,
+// and test it using Contains(). The candidate interval is the interval with the
+// largest min() having min() <= value.
+//
+// Determining the candidate interval takes a couple of steps. First, since the
+// underlying std::set stores intervals, not values, we need to create a "probe
+// interval" suitable for use as a search key. The probe interval used is
+// [value, value). Now we can restate the problem as finding the largest
+// interval in the QuicIntervalSet that is <= the probe interval.
+//
+// This restatement only works if the set's comparator behaves in a certain way.
+// In particular it needs to order first by ascending min(), and then by
+// descending max(). The comparator used by this library is defined in exactly
+// this way. To see why descending max() is required, consider the following
+// example. Assume an QuicIntervalSet containing these intervals:
+//
+//   [0, 5)  [10, 20)  [50, 60)
+//
+// Consider searching for the value 15. The probe interval [15, 15) is created,
+// and [10, 20) is identified as the largest interval in the set <= the probe
+// interval. This is the correct interval needed for the Contains() test, which
+// will then return true.
+//
+// Now consider searching for the value 30. The probe interval [30, 30) is
+// created, and again [10, 20] is identified as the largest interval <= the
+// probe interval. This is again the correct interval needed for the Contains()
+// test, which in this case returns false.
+//
+// Finally, consider searching for the value 10. The probe interval [10, 10) is
+// created. Here the ordering relationship between [10, 10) and [10, 20) becomes
+// vitally important. If [10, 10) were to come before [10, 20), then [0, 5)
+// would be the largest interval <= the probe, leading to the wrong choice of
+// interval for the Contains() test. Therefore [10, 10) needs to come after
+// [10, 20). The simplest way to make this work in the general case is to order
+// by ascending min() but descending max(). In this ordering, the empty interval
+// is larger than any non-empty interval with the same min(). The comparator
+// used by this library is careful to induce this ordering.
+//
+// Another detail involves the choice of which std::set method to use to try to
+// find the candidate interval. The most appropriate entry point is
+// set::upper_bound(), which finds the smallest interval which is > the probe
+// interval. The semantics of upper_bound() are slightly different from what we
+// want (namely, to find the largest interval which is <= the probe interval)
+// but they are close enough; the interval found by upper_bound() will always be
+// one step past the interval we are looking for (if it exists) or at begin()
+// (if it does not). Getting to the proper interval is a simple matter of
+// decrementing the iterator.
+template <typename T>
+typename QuicIntervalSet<T>::const_iterator QuicIntervalSet<T>::Find(
+    const T& value) const {
+  value_type tmp(value, value);
+  const_iterator it = intervals_.upper_bound(tmp);
+  if (it == intervals_.begin())
+    return intervals_.end();
+  --it;
+  if (it->Contains(value))
+    return it;
+  else
+    return intervals_.end();
+}
+
+// This method finds the interval that Contains() the interval "probe", if such
+// an interval exists in the QuicIntervalSet. The way this is done is to locate
+// the "candidate interval", the only interval that could *possibly* contain
+// "probe", and test it using Contains(). The candidate interval is the largest
+// interval that is <= the probe interval.
+//
+// The search for the candidate interval only works if the comparator used
+// behaves in a certain way. In particular it needs to order first by ascending
+// min(), and then by descending max(). The comparator used by this library is
+// defined in exactly this way. To see why descending max() is required,
+// consider the following example. Assume an QuicIntervalSet containing these
+// intervals:
+//
+//   [0, 5)  [10, 20)  [50, 60)
+//
+// Consider searching for the probe [15, 17). [10, 20) is the largest interval
+// in the set which is <= the probe interval. This is the correct interval
+// needed for the Contains() test, which will then return true, because [10, 20)
+// contains [15, 17).
+//
+// Now consider searching for the probe [30, 32). Again [10, 20] is the largest
+// interval <= the probe interval. This is again the correct interval needed for
+// the Contains() test, which in this case returns false, because [10, 20) does
+// not contain [30, 32).
+//
+// Finally, consider searching for the probe [10, 12). Here the ordering
+// relationship between [10, 12) and [10, 20) becomes vitally important. If
+// [10, 12) were to come before [10, 20), then [0, 5) would be the largest
+// interval <= the probe, leading to the wrong choice of interval for the
+// Contains() test. Therefore [10, 12) needs to come after [10, 20). The
+// simplest way to make this work in the general case is to order by ascending
+// min() but descending max(). In this ordering, given two intervals with the
+// same min(), the wider one goes before the narrower one. The comparator used
+// by this library is careful to induce this ordering.
+//
+// Another detail involves the choice of which std::set method to use to try to
+// find the candidate interval. The most appropriate entry point is
+// set::upper_bound(), which finds the smallest interval which is > the probe
+// interval. The semantics of upper_bound() are slightly different from what we
+// want (namely, to find the largest interval which is <= the probe interval)
+// but they are close enough; the interval found by upper_bound() will always be
+// one step past the interval we are looking for (if it exists) or at begin()
+// (if it does not). Getting to the proper interval is a simple matter of
+// decrementing the iterator.
+template <typename T>
+typename QuicIntervalSet<T>::const_iterator QuicIntervalSet<T>::Find(
+    const value_type& probe) const {
+  const_iterator it = intervals_.upper_bound(probe);
+  if (it == intervals_.begin())
+    return intervals_.end();
+  --it;
+  if (it->Contains(probe))
+    return it;
+  else
+    return intervals_.end();
+}
+
+template <typename T>
+typename QuicIntervalSet<T>::const_iterator QuicIntervalSet<T>::LowerBound(
+    const T& value) const {
+  const_iterator it = intervals_.lower_bound(value_type(value, value));
+  if (it == intervals_.begin()) {
+    return it;
+  }
+
+  // The previous intervals_.lower_bound() checking is essentially based on
+  // interval.min(), so we need to check whether the `value` is contained in
+  // the previous interval.
+  --it;
+  if (it->Contains(value)) {
+    return it;
+  } else {
+    return ++it;
+  }
+}
+
+template <typename T>
+typename QuicIntervalSet<T>::const_iterator QuicIntervalSet<T>::UpperBound(
+    const T& value) const {
+  return intervals_.upper_bound(value_type(value, value));
+}
+
+template <typename T>
+bool QuicIntervalSet<T>::IsDisjoint(const value_type& interval) const {
+  if (interval.Empty())
+    return true;
+  value_type tmp(interval.min(), interval.min());
+  // Find the first interval with min() > interval.min()
+  const_iterator it = intervals_.upper_bound(tmp);
+  if (it != intervals_.end() && interval.max() > it->min())
+    return false;
+  if (it == intervals_.begin())
+    return true;
+  --it;
+  return it->max() <= interval.min();
+}
+
+template <typename T>
+void QuicIntervalSet<T>::Union(const QuicIntervalSet& other) {
+  intervals_.insert(other.begin(), other.end());
+  Compact(intervals_.begin(), intervals_.end());
+}
+
+template <typename T>
+typename QuicIntervalSet<T>::const_iterator
+QuicIntervalSet<T>::FindIntersectionCandidate(
+    const QuicIntervalSet& other) const {
+  return FindIntersectionCandidate(*other.intervals_.begin());
+}
+
+template <typename T>
+typename QuicIntervalSet<T>::const_iterator
+QuicIntervalSet<T>::FindIntersectionCandidate(
+    const value_type& interval) const {
+  // Use upper_bound to efficiently find the first interval in intervals_
+  // where min() is greater than interval.min().  If the result
+  // isn't the beginning of intervals_ then move backwards one interval since
+  // the interval before it is the first candidate where max() may be
+  // greater than interval.min().
+  // In other words, no interval before that can possibly intersect with any
+  // of other.intervals_.
+  const_iterator mine = intervals_.upper_bound(interval);
+  if (mine != intervals_.begin()) {
+    --mine;
+  }
+  return mine;
+}
+
+template <typename T>
+template <typename X, typename Func>
+bool QuicIntervalSet<T>::FindNextIntersectingPairImpl(X* x,
+                                                      const QuicIntervalSet& y,
+                                                      const_iterator* mine,
+                                                      const_iterator* theirs,
+                                                      Func on_hole) {
+  CHECK(x != nullptr);
+  if ((*mine == x->intervals_.end()) || (*theirs == y.intervals_.end())) {
+    return false;
+  }
+  while (!(**mine).Intersects(**theirs)) {
+    const_iterator erase_first = *mine;
+    // Skip over intervals in 'mine' that don't reach 'theirs'.
+    while (*mine != x->intervals_.end() && (**mine).max() <= (**theirs).min()) {
+      ++(*mine);
+    }
+    on_hole(x, erase_first, *mine);
+    // We're done if the end of intervals_ is reached.
+    if (*mine == x->intervals_.end()) {
+      return false;
+    }
+    // Skip over intervals 'theirs' that don't reach 'mine'.
+    while (*theirs != y.intervals_.end() &&
+           (**theirs).max() <= (**mine).min()) {
+      ++(*theirs);
+    }
+    // If the end of other.intervals_ is reached, we're done.
+    if (*theirs == y.intervals_.end()) {
+      on_hole(x, *mine, x->intervals_.end());
+      return false;
+    }
+  }
+  return true;
+}
+
+template <typename T>
+void QuicIntervalSet<T>::Intersection(const QuicIntervalSet& other) {
+  if (!SpanningInterval().Intersects(other.SpanningInterval())) {
+    intervals_.clear();
+    return;
+  }
+
+  const_iterator mine = FindIntersectionCandidate(other);
+  // Remove any intervals that cannot possibly intersect with other.intervals_.
+  intervals_.erase(intervals_.begin(), mine);
+  const_iterator theirs = other.FindIntersectionCandidate(*this);
+
+  while (FindNextIntersectingPairAndEraseHoles(other, &mine, &theirs)) {
+    // OK, *mine and *theirs intersect.  Now, we find the largest
+    // span of intervals in other (starting at theirs) - say [a..b]
+    // - that intersect *mine, and we replace *mine with (*mine
+    // intersect x) for all x in [a..b] Note that subsequent
+    // intervals in this can't intersect any intervals in [a..b) --
+    // they may only intersect b or subsequent intervals in other.
+    value_type i(*mine);
+    intervals_.erase(mine);
+    mine = intervals_.end();
+    value_type intersection;
+    while (theirs != other.intervals_.end() &&
+           i.Intersects(*theirs, &intersection)) {
+      std::pair<typename Set::iterator, bool> ins =
+          intervals_.insert(intersection);
+      DCHECK(ins.second);
+      mine = ins.first;
+      ++theirs;
+    }
+    DCHECK(mine != intervals_.end());
+    --theirs;
+    ++mine;
+  }
+  DCHECK(Valid());
+}
+
+template <typename T>
+bool QuicIntervalSet<T>::Intersects(const QuicIntervalSet& other) const {
+  if (!SpanningInterval().Intersects(other.SpanningInterval())) {
+    return false;
+  }
+
+  const_iterator mine = FindIntersectionCandidate(other);
+  if (mine == intervals_.end()) {
+    return false;
+  }
+  const_iterator theirs = other.FindIntersectionCandidate(*mine);
+
+  return FindNextIntersectingPair(other, &mine, &theirs);
+}
+
+template <typename T>
+void QuicIntervalSet<T>::Difference(const value_type& interval) {
+  if (!SpanningInterval().Intersects(interval)) {
+    return;
+  }
+  Difference(QuicIntervalSet<T>(interval));
+}
+
+template <typename T>
+void QuicIntervalSet<T>::Difference(const T& min, const T& max) {
+  Difference(value_type(min, max));
+}
+
+template <typename T>
+void QuicIntervalSet<T>::Difference(const QuicIntervalSet& other) {
+  if (!SpanningInterval().Intersects(other.SpanningInterval())) {
+    return;
+  }
+
+  const_iterator mine = FindIntersectionCandidate(other);
+  // If no interval in mine reaches the first interval of theirs then we're
+  // done.
+  if (mine == intervals_.end()) {
+    return;
+  }
+  const_iterator theirs = other.FindIntersectionCandidate(*this);
+
+  while (FindNextIntersectingPair(other, &mine, &theirs)) {
+    // At this point *mine and *theirs overlap.  Remove mine from
+    // intervals_ and replace it with the possibly two intervals that are
+    // the difference between mine and theirs.
+    value_type i(*mine);
+    intervals_.erase(mine++);
+    value_type lo;
+    value_type hi;
+    i.Difference(*theirs, &lo, &hi);
+
+    if (!lo.Empty()) {
+      // We have a low end.  This can't intersect anything else.
+      std::pair<typename Set::iterator, bool> ins = intervals_.insert(lo);
+      DCHECK(ins.second);
+    }
+
+    if (!hi.Empty()) {
+      std::pair<typename Set::iterator, bool> ins = intervals_.insert(hi);
+      DCHECK(ins.second);
+      mine = ins.first;
+    }
+  }
+  DCHECK(Valid());
+}
+
+template <typename T>
+void QuicIntervalSet<T>::Complement(const T& min, const T& max) {
+  QuicIntervalSet<T> span(min, max);
+  span.Difference(*this);
+  intervals_.swap(span.intervals_);
+}
+
+template <typename T>
+string QuicIntervalSet<T>::ToString() const {
+  std::ostringstream os;
+  os << *this;
+  return os.str();
+}
+
+// This method compacts the QuicIntervalSet, merging pairs of overlapping
+// intervals into a single interval. In the steady state, the QuicIntervalSet
+// does not contain any such pairs. However, the way the Union() and Add()
+// methods work is to temporarily put the QuicIntervalSet into such a state and
+// then to call Compact() to "fix it up" so that it is no longer in that state.
+//
+// Compact() needs the interval set to allow two intervals [a,b) and [a,c)
+// (having the same min() but different max()) to briefly coexist in the set at
+// the same time, and be adjacent to each other, so that they can be efficiently
+// located and merged into a single interval. This state would be impossible
+// with a comparator which only looked at min(), as such a comparator would
+// consider such pairs equal. Fortunately, the comparator used by
+// QuicIntervalSet does exactly what is needed, ordering first by ascending
+// min(), then by descending max().
+template <typename T>
+void QuicIntervalSet<T>::Compact(const typename Set::iterator& begin,
+                                 const typename Set::iterator& end) {
+  if (begin == end)
+    return;
+  typename Set::iterator next = begin;
+  typename Set::iterator prev = begin;
+  typename Set::iterator it = begin;
+  ++it;
+  ++next;
+  while (it != end) {
+    ++next;
+    if (prev->max() >= it->min()) {
+      // Overlapping / coalesced range; merge the two intervals.
+      T min = prev->min();
+      T max = std::max(prev->max(), it->max());
+      value_type i(min, max);
+      intervals_.erase(prev);
+      intervals_.erase(it);
+      std::pair<typename Set::iterator, bool> ins = intervals_.insert(i);
+      DCHECK(ins.second);
+      prev = ins.first;
+    } else {
+      prev = it;
+    }
+    it = next;
+  }
+}
+
+template <typename T>
+bool QuicIntervalSet<T>::Valid() const {
+  const_iterator prev = end();
+  for (const_iterator it = begin(); it != end(); ++it) {
+    // invalid or empty interval.
+    if (it->min() >= it->max())
+      return false;
+    // Not sorted, not disjoint, or adjacent.
+    if (prev != end() && prev->max() >= it->min())
+      return false;
+    prev = it;
+  }
+  return true;
+}
+
+template <typename T>
+void swap(QuicIntervalSet<T>& x, QuicIntervalSet<T>& y) {
+  x.Swap(&y);
+}
+
+// This comparator orders intervals first by ascending min() and then by
+// descending max(). Readers who are satisified with that explanation can stop
+// reading here. The remainder of this comment is for the benefit of future
+// maintainers of this library.
+//
+// The reason for this ordering is that this comparator has to serve two
+// masters. First, it has to maintain the intervals in its internal set in the
+// order that clients expect to see them. Clients see these intervals via the
+// iterators provided by begin()/end() or as a result of invoking Get(). For
+// this reason, the comparator orders intervals by ascending min().
+//
+// If client iteration were the only consideration, then ordering by ascending
+// min() would be good enough. This is because the intervals in the
+// QuicIntervalSet are non-empty, non-adjacent, and mutually disjoint; such
+// intervals happen to always have disjoint min() values, so such a comparator
+// would never even have to look at max() in order to work correctly for this
+// class.
+//
+// However, in addition to ordering by ascending min(), this comparator also has
+// a second responsibility: satisfying the special needs of this library's
+// peculiar internal implementation. These needs require the comparator to order
+// first by ascending min() and then by descending max(). The best way to
+// understand why this is so is to check out the comments associated with the
+// Find() and Compact() methods.
+template <typename T>
+bool QuicIntervalSet<T>::IntervalLess::operator()(const value_type& a,
+                                                  const value_type& b) const {
+  return a.min() < b.min() || (a.min() == b.min() && a.max() > b.max());
+}
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_CORE_QUIC_INTERVAL_SET_H_
diff --git a/quic/core/quic_interval_set_test.cc b/quic/core/quic_interval_set_test.cc
new file mode 100644
index 0000000..65d1d32
--- /dev/null
+++ b/quic/core/quic_interval_set_test.cc
@@ -0,0 +1,979 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_interval_set.h"
+
+#include <stdarg.h>
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+#include <limits>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+using ::testing::ElementsAreArray;
+
+class QuicIntervalSetTest : public QuicTest {
+ protected:
+  virtual void SetUp() {
+    // Initialize two QuicIntervalSets for union, intersection, and difference
+    // tests
+    is.Add(100, 200);
+    is.Add(300, 400);
+    is.Add(500, 600);
+    is.Add(700, 800);
+    is.Add(900, 1000);
+    is.Add(1100, 1200);
+    is.Add(1300, 1400);
+    is.Add(1500, 1600);
+    is.Add(1700, 1800);
+    is.Add(1900, 2000);
+    is.Add(2100, 2200);
+
+    // Lots of different cases:
+    other.Add(50, 70);      // disjoint, at the beginning
+    other.Add(2250, 2270);  // disjoint, at the end
+    other.Add(650, 670);    // disjoint, in the middle
+    other.Add(350, 360);    // included
+    other.Add(370, 380);    // also included (two at once)
+    other.Add(470, 530);    // overlaps low end
+    other.Add(770, 830);    // overlaps high end
+    other.Add(870, 900);    // meets at low end
+    other.Add(1200, 1230);  // meets at high end
+    other.Add(1270, 1830);  // overlaps multiple ranges
+  }
+
+  virtual void TearDown() {
+    is.Clear();
+    EXPECT_TRUE(is.Empty());
+    other.Clear();
+    EXPECT_TRUE(other.Empty());
+  }
+  QuicIntervalSet<int> is;
+  QuicIntervalSet<int> other;
+};
+
+TEST_F(QuicIntervalSetTest, IsDisjoint) {
+  EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(0, 99)));
+  EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(0, 100)));
+  EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(200, 200)));
+  EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(200, 299)));
+  EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(400, 407)));
+  EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(405, 499)));
+  EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(2300, 2300)));
+  EXPECT_TRUE(
+      is.IsDisjoint(QuicInterval<int>(2300, std::numeric_limits<int>::max())));
+  EXPECT_FALSE(is.IsDisjoint(QuicInterval<int>(100, 105)));
+  EXPECT_FALSE(is.IsDisjoint(QuicInterval<int>(199, 300)));
+  EXPECT_FALSE(is.IsDisjoint(QuicInterval<int>(250, 450)));
+  EXPECT_FALSE(is.IsDisjoint(QuicInterval<int>(299, 400)));
+  EXPECT_FALSE(is.IsDisjoint(QuicInterval<int>(250, 2000)));
+  EXPECT_FALSE(
+      is.IsDisjoint(QuicInterval<int>(2199, std::numeric_limits<int>::max())));
+  // Empty intervals.
+  EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(90, 90)));
+  EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(100, 100)));
+  EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(100, 90)));
+  EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(150, 150)));
+  EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(200, 200)));
+  EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(400, 300)));
+}
+
+// Base helper method for verifying the contents of an interval set.
+// Returns true iff <is> contains <count> intervals whose successive
+// endpoints match the sequence of args in <ap>:
+static bool VA_Check(const QuicIntervalSet<int>& is, int count, va_list ap) {
+  std::vector<QuicInterval<int>> intervals(is.begin(), is.end());
+  if (count != static_cast<int>(intervals.size())) {
+    LOG(ERROR) << "Expected " << count << " intervals, got " << intervals.size()
+               << ": " << is;
+    return false;
+  }
+  if (count != static_cast<int>(is.Size())) {
+    LOG(ERROR) << "Expected " << count << " intervals, got Size " << is.Size()
+               << ": " << is;
+    return false;
+  }
+  bool result = true;
+  for (int i = 0; i < count; i++) {
+    int min = va_arg(ap, int);
+    int max = va_arg(ap, int);
+    if (min != intervals[i].min() || max != intervals[i].max()) {
+      LOG(ERROR) << "Expected: [" << min << ", " << max << ") got "
+                 << intervals[i] << " in " << is;
+      result = false;
+    }
+  }
+  return result;
+}
+
+static bool Check(const QuicIntervalSet<int>& is, int count, ...) {
+  va_list ap;
+  va_start(ap, count);
+  const bool result = VA_Check(is, count, ap);
+  va_end(ap);
+  return result;
+}
+
+// Some helper functions for testing Contains and Find, which are logically the
+// same.
+static void TestContainsAndFind(const QuicIntervalSet<int>& is, int value) {
+  EXPECT_TRUE(is.Contains(value)) << "Set does not contain " << value;
+  auto it = is.Find(value);
+  EXPECT_NE(it, is.end()) << "No iterator to interval containing " << value;
+  EXPECT_TRUE(it->Contains(value)) << "Iterator does not contain " << value;
+}
+
+static void TestContainsAndFind(const QuicIntervalSet<int>& is,
+                                int min,
+                                int max) {
+  EXPECT_TRUE(is.Contains(min, max))
+      << "Set does not contain interval with min " << min << "and max " << max;
+  auto it = is.Find(min, max);
+  EXPECT_NE(it, is.end()) << "No iterator to interval with min " << min
+                          << "and max " << max;
+  EXPECT_TRUE(it->Contains(QuicInterval<int>(min, max)))
+      << "Iterator does not contain interval with min " << min << "and max "
+      << max;
+}
+
+static void TestNotContainsAndFind(const QuicIntervalSet<int>& is, int value) {
+  EXPECT_FALSE(is.Contains(value)) << "Set contains " << value;
+  auto it = is.Find(value);
+  EXPECT_EQ(it, is.end()) << "There is iterator to interval containing "
+                          << value;
+}
+
+static void TestNotContainsAndFind(const QuicIntervalSet<int>& is,
+                                   int min,
+                                   int max) {
+  EXPECT_FALSE(is.Contains(min, max))
+      << "Set contains interval with min " << min << "and max " << max;
+  auto it = is.Find(min, max);
+  EXPECT_EQ(it, is.end()) << "There is iterator to interval with min " << min
+                          << "and max " << max;
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetBasic) {
+  // Test Add, Get, Contains and Find
+  QuicIntervalSet<int> iset;
+  EXPECT_TRUE(iset.Empty());
+  EXPECT_EQ(0, iset.Size());
+  iset.Add(100, 200);
+  EXPECT_FALSE(iset.Empty());
+  EXPECT_EQ(1, iset.Size());
+  iset.Add(100, 150);
+  iset.Add(150, 200);
+  iset.Add(130, 170);
+  iset.Add(90, 150);
+  iset.Add(170, 220);
+  iset.Add(300, 400);
+  iset.Add(250, 450);
+  EXPECT_FALSE(iset.Empty());
+  EXPECT_EQ(2, iset.Size());
+  EXPECT_TRUE(Check(iset, 2, 90, 220, 250, 450));
+
+  // Test two intervals with a.max == b.min, that will just join up.
+  iset.Clear();
+  iset.Add(100, 200);
+  iset.Add(200, 300);
+  EXPECT_FALSE(iset.Empty());
+  EXPECT_EQ(1, iset.Size());
+  EXPECT_TRUE(Check(iset, 1, 100, 300));
+
+  // Test adding two sets together.
+  iset.Clear();
+  QuicIntervalSet<int> iset_add;
+  iset.Add(100, 200);
+  iset.Add(100, 150);
+  iset.Add(150, 200);
+  iset.Add(130, 170);
+  iset_add.Add(90, 150);
+  iset_add.Add(170, 220);
+  iset_add.Add(300, 400);
+  iset_add.Add(250, 450);
+
+  iset.Add(iset_add);
+  EXPECT_FALSE(iset.Empty());
+  EXPECT_EQ(2, iset.Size());
+  EXPECT_TRUE(Check(iset, 2, 90, 220, 250, 450));
+
+  // Test begin()/end(), and rbegin()/rend()
+  // to iterate over intervals.
+  {
+    std::vector<QuicInterval<int>> expected(iset.begin(), iset.end());
+
+    std::vector<QuicInterval<int>> actual1;
+    std::copy(iset.begin(), iset.end(), back_inserter(actual1));
+    ASSERT_EQ(expected.size(), actual1.size());
+
+    std::vector<QuicInterval<int>> actual2;
+    std::copy(iset.begin(), iset.end(), back_inserter(actual2));
+    ASSERT_EQ(expected.size(), actual2.size());
+
+    for (size_t i = 0; i < expected.size(); i++) {
+      EXPECT_EQ(expected[i].min(), actual1[i].min());
+      EXPECT_EQ(expected[i].max(), actual1[i].max());
+
+      EXPECT_EQ(expected[i].min(), actual2[i].min());
+      EXPECT_EQ(expected[i].max(), actual2[i].max());
+    }
+
+    // Ensure that the rbegin()/rend() iterators correctly yield the intervals
+    // in reverse order.
+    EXPECT_THAT(std::vector<QuicInterval<int>>(iset.rbegin(), iset.rend()),
+                ElementsAreArray(expected.rbegin(), expected.rend()));
+  }
+
+  TestNotContainsAndFind(iset, 89);
+  TestContainsAndFind(iset, 90);
+  TestContainsAndFind(iset, 120);
+  TestContainsAndFind(iset, 219);
+  TestNotContainsAndFind(iset, 220);
+  TestNotContainsAndFind(iset, 235);
+  TestNotContainsAndFind(iset, 249);
+  TestContainsAndFind(iset, 250);
+  TestContainsAndFind(iset, 300);
+  TestContainsAndFind(iset, 449);
+  TestNotContainsAndFind(iset, 450);
+  TestNotContainsAndFind(iset, 451);
+
+  TestNotContainsAndFind(iset, 50, 60);
+  TestNotContainsAndFind(iset, 50, 90);
+  TestNotContainsAndFind(iset, 50, 200);
+  TestNotContainsAndFind(iset, 90, 90);
+  TestContainsAndFind(iset, 90, 200);
+  TestContainsAndFind(iset, 100, 200);
+  TestContainsAndFind(iset, 100, 220);
+  TestNotContainsAndFind(iset, 100, 221);
+  TestNotContainsAndFind(iset, 220, 220);
+  TestNotContainsAndFind(iset, 240, 300);
+  TestContainsAndFind(iset, 250, 300);
+  TestContainsAndFind(iset, 260, 300);
+  TestContainsAndFind(iset, 300, 450);
+  TestNotContainsAndFind(iset, 300, 451);
+
+  QuicIntervalSet<int> iset_contains;
+  iset_contains.Add(50, 90);
+  EXPECT_FALSE(iset.Contains(iset_contains));
+  iset_contains.Clear();
+
+  iset_contains.Add(90, 200);
+  EXPECT_TRUE(iset.Contains(iset_contains));
+  iset_contains.Add(100, 200);
+  EXPECT_TRUE(iset.Contains(iset_contains));
+  iset_contains.Add(100, 220);
+  EXPECT_TRUE(iset.Contains(iset_contains));
+  iset_contains.Add(250, 300);
+  EXPECT_TRUE(iset.Contains(iset_contains));
+  iset_contains.Add(300, 450);
+  EXPECT_TRUE(iset.Contains(iset_contains));
+  iset_contains.Add(300, 451);
+  EXPECT_FALSE(iset.Contains(iset_contains));
+  EXPECT_FALSE(iset.Contains(QuicInterval<int>()));
+  EXPECT_FALSE(iset.Contains(QuicIntervalSet<int>()));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetContainsEmpty) {
+  const QuicIntervalSet<int> empty;
+  const QuicIntervalSet<int> other_empty;
+  const QuicIntervalSet<int> non_empty({{10, 20}, {40, 50}});
+  EXPECT_FALSE(empty.Contains(empty));
+  EXPECT_FALSE(empty.Contains(other_empty));
+  EXPECT_FALSE(empty.Contains(non_empty));
+  EXPECT_FALSE(non_empty.Contains(empty));
+}
+
+TEST_F(QuicIntervalSetTest, Equality) {
+  QuicIntervalSet<int> is_copy = is;
+  EXPECT_EQ(is, is);
+  EXPECT_EQ(is, is_copy);
+  EXPECT_NE(is, other);
+  EXPECT_NE(is, QuicIntervalSet<int>());
+  EXPECT_EQ(QuicIntervalSet<int>(), QuicIntervalSet<int>());
+}
+
+TEST_F(QuicIntervalSetTest, LowerAndUpperBound) {
+  QuicIntervalSet<int> intervals;
+  intervals.Add(10, 20);
+  intervals.Add(30, 40);
+
+  //   [10, 20)  [30, 40)  end
+  //   ^                        LowerBound(5)
+  //   ^                        LowerBound(10)
+  //   ^                        LowerBound(15)
+  //             ^              LowerBound(20)
+  //             ^              LowerBound(25)
+  //             ^              LowerBound(30)
+  //             ^              LowerBound(35)
+  //                       ^    LowerBound(40)
+  //                       ^    LowerBound(50)
+  EXPECT_EQ(intervals.LowerBound(5)->min(), 10);
+  EXPECT_EQ(intervals.LowerBound(10)->min(), 10);
+  EXPECT_EQ(intervals.LowerBound(15)->min(), 10);
+  EXPECT_EQ(intervals.LowerBound(20)->min(), 30);
+  EXPECT_EQ(intervals.LowerBound(25)->min(), 30);
+  EXPECT_EQ(intervals.LowerBound(30)->min(), 30);
+  EXPECT_EQ(intervals.LowerBound(35)->min(), 30);
+  EXPECT_EQ(intervals.LowerBound(40), intervals.end());
+  EXPECT_EQ(intervals.LowerBound(50), intervals.end());
+
+  //   [10, 20)  [30, 40)  end
+  //   ^                        UpperBound(5)
+  //             ^              UpperBound(10)
+  //             ^              UpperBound(15)
+  //             ^              UpperBound(20)
+  //             ^              UpperBound(25)
+  //                       ^    UpperBound(30)
+  //                       ^    UpperBound(35)
+  //                       ^    UpperBound(40)
+  //                       ^    UpperBound(50)
+  EXPECT_EQ(intervals.UpperBound(5)->min(), 10);
+  EXPECT_EQ(intervals.UpperBound(10)->min(), 30);
+  EXPECT_EQ(intervals.UpperBound(15)->min(), 30);
+  EXPECT_EQ(intervals.UpperBound(20)->min(), 30);
+  EXPECT_EQ(intervals.UpperBound(25)->min(), 30);
+  EXPECT_EQ(intervals.UpperBound(30), intervals.end());
+  EXPECT_EQ(intervals.UpperBound(35), intervals.end());
+  EXPECT_EQ(intervals.UpperBound(40), intervals.end());
+  EXPECT_EQ(intervals.UpperBound(50), intervals.end());
+}
+
+TEST_F(QuicIntervalSetTest, SpanningInterval) {
+  // Spanning interval of an empty set is empty:
+  {
+    QuicIntervalSet<int> iset;
+    const QuicInterval<int>& ival = iset.SpanningInterval();
+    EXPECT_TRUE(ival.Empty());
+  }
+
+  // Spanning interval of a set with one interval is that interval:
+  {
+    QuicIntervalSet<int> iset;
+    iset.Add(100, 200);
+    const QuicInterval<int>& ival = iset.SpanningInterval();
+    EXPECT_EQ(100, ival.min());
+    EXPECT_EQ(200, ival.max());
+  }
+
+  // Spanning interval of a set with multiple elements is determined
+  // by the endpoints of the first and last element:
+  {
+    const QuicInterval<int>& ival = is.SpanningInterval();
+    EXPECT_EQ(100, ival.min());
+    EXPECT_EQ(2200, ival.max());
+  }
+  {
+    const QuicInterval<int>& ival = other.SpanningInterval();
+    EXPECT_EQ(50, ival.min());
+    EXPECT_EQ(2270, ival.max());
+  }
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetUnion) {
+  is.Union(other);
+  EXPECT_TRUE(Check(is, 12, 50, 70, 100, 200, 300, 400, 470, 600, 650, 670, 700,
+                    830, 870, 1000, 1100, 1230, 1270, 1830, 1900, 2000, 2100,
+                    2200, 2250, 2270));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersection) {
+  EXPECT_TRUE(is.Intersects(other));
+  EXPECT_TRUE(other.Intersects(is));
+  is.Intersection(other);
+  EXPECT_TRUE(Check(is, 7, 350, 360, 370, 380, 500, 530, 770, 800, 1300, 1400,
+                    1500, 1600, 1700, 1800));
+  EXPECT_TRUE(is.Intersects(other));
+  EXPECT_TRUE(other.Intersects(is));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionBothEmpty) {
+  QuicIntervalSet<string> mine, theirs;
+  EXPECT_FALSE(mine.Intersects(theirs));
+  EXPECT_FALSE(theirs.Intersects(mine));
+  mine.Intersection(theirs);
+  EXPECT_TRUE(mine.Empty());
+  EXPECT_FALSE(mine.Intersects(theirs));
+  EXPECT_FALSE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionEmptyMine) {
+  QuicIntervalSet<string> mine;
+  QuicIntervalSet<string> theirs("a", "b");
+  EXPECT_FALSE(mine.Intersects(theirs));
+  EXPECT_FALSE(theirs.Intersects(mine));
+  mine.Intersection(theirs);
+  EXPECT_TRUE(mine.Empty());
+  EXPECT_FALSE(mine.Intersects(theirs));
+  EXPECT_FALSE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionEmptyTheirs) {
+  QuicIntervalSet<string> mine("a", "b");
+  QuicIntervalSet<string> theirs;
+  EXPECT_FALSE(mine.Intersects(theirs));
+  EXPECT_FALSE(theirs.Intersects(mine));
+  mine.Intersection(theirs);
+  EXPECT_TRUE(mine.Empty());
+  EXPECT_FALSE(mine.Intersects(theirs));
+  EXPECT_FALSE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionTheirsBeforeMine) {
+  QuicIntervalSet<string> mine("y", "z");
+  QuicIntervalSet<string> theirs;
+  theirs.Add("a", "b");
+  theirs.Add("c", "d");
+  EXPECT_FALSE(mine.Intersects(theirs));
+  EXPECT_FALSE(theirs.Intersects(mine));
+  mine.Intersection(theirs);
+  EXPECT_TRUE(mine.Empty());
+  EXPECT_FALSE(mine.Intersects(theirs));
+  EXPECT_FALSE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionMineBeforeTheirs) {
+  QuicIntervalSet<string> mine;
+  mine.Add("a", "b");
+  mine.Add("c", "d");
+  QuicIntervalSet<string> theirs("y", "z");
+  EXPECT_FALSE(mine.Intersects(theirs));
+  EXPECT_FALSE(theirs.Intersects(mine));
+  mine.Intersection(theirs);
+  EXPECT_TRUE(mine.Empty());
+  EXPECT_FALSE(mine.Intersects(theirs));
+  EXPECT_FALSE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest,
+       QuicIntervalSetIntersectionTheirsBeforeMineInt64Singletons) {
+  QuicIntervalSet<int64_t> mine({{10, 15}});
+  QuicIntervalSet<int64_t> theirs({{-20, -5}});
+  EXPECT_FALSE(mine.Intersects(theirs));
+  EXPECT_FALSE(theirs.Intersects(mine));
+  mine.Intersection(theirs);
+  EXPECT_TRUE(mine.Empty());
+  EXPECT_FALSE(mine.Intersects(theirs));
+  EXPECT_FALSE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest,
+       QuicIntervalSetIntersectionMineBeforeTheirsIntSingletons) {
+  QuicIntervalSet<int> mine({{10, 15}});
+  QuicIntervalSet<int> theirs({{90, 95}});
+  EXPECT_FALSE(mine.Intersects(theirs));
+  EXPECT_FALSE(theirs.Intersects(mine));
+  mine.Intersection(theirs);
+  EXPECT_TRUE(mine.Empty());
+  EXPECT_FALSE(mine.Intersects(theirs));
+  EXPECT_FALSE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionTheirsBetweenMine) {
+  QuicIntervalSet<int64_t> mine({{0, 5}, {40, 50}});
+  QuicIntervalSet<int64_t> theirs({{10, 15}});
+  EXPECT_FALSE(mine.Intersects(theirs));
+  EXPECT_FALSE(theirs.Intersects(mine));
+  mine.Intersection(theirs);
+  EXPECT_TRUE(mine.Empty());
+  EXPECT_FALSE(mine.Intersects(theirs));
+  EXPECT_FALSE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionMineBetweenTheirs) {
+  QuicIntervalSet<int> mine({{20, 25}});
+  QuicIntervalSet<int> theirs({{10, 15}, {30, 32}});
+  EXPECT_FALSE(mine.Intersects(theirs));
+  EXPECT_FALSE(theirs.Intersects(mine));
+  mine.Intersection(theirs);
+  EXPECT_TRUE(mine.Empty());
+  EXPECT_FALSE(mine.Intersects(theirs));
+  EXPECT_FALSE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionAlternatingIntervals) {
+  QuicIntervalSet<int> mine, theirs;
+  mine.Add(10, 20);
+  mine.Add(40, 50);
+  mine.Add(60, 70);
+  theirs.Add(25, 39);
+  theirs.Add(55, 59);
+  theirs.Add(75, 79);
+  EXPECT_FALSE(mine.Intersects(theirs));
+  EXPECT_FALSE(theirs.Intersects(mine));
+  mine.Intersection(theirs);
+  EXPECT_TRUE(mine.Empty());
+  EXPECT_FALSE(mine.Intersects(theirs));
+  EXPECT_FALSE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest,
+       QuicIntervalSetIntersectionAdjacentAlternatingNonIntersectingIntervals) {
+  // Make sure that intersection with adjacent interval set is empty.
+  const QuicIntervalSet<int> x1({{0, 10}});
+  const QuicIntervalSet<int> y1({{-50, 0}, {10, 95}});
+
+  QuicIntervalSet<int> result1 = x1;
+  result1.Intersection(y1);
+  EXPECT_TRUE(result1.Empty()) << result1;
+
+  const QuicIntervalSet<int16_t> x2({{0, 10}, {20, 30}, {40, 90}});
+  const QuicIntervalSet<int16_t> y2(
+      {{-50, -40}, {-2, 0}, {10, 20}, {32, 40}, {90, 95}});
+
+  QuicIntervalSet<int16_t> result2 = x2;
+  result2.Intersection(y2);
+  EXPECT_TRUE(result2.Empty()) << result2;
+
+  const QuicIntervalSet<int64_t> x3({{-1, 5}, {5, 10}});
+  const QuicIntervalSet<int64_t> y3({{-10, -1}, {10, 95}});
+
+  QuicIntervalSet<int64_t> result3 = x3;
+  result3.Intersection(y3);
+  EXPECT_TRUE(result3.Empty()) << result3;
+}
+
+TEST_F(QuicIntervalSetTest,
+       QuicIntervalSetIntersectionAlternatingIntersectingIntervals) {
+  const QuicIntervalSet<int> x1({{0, 10}});
+  const QuicIntervalSet<int> y1({{-50, 1}, {9, 95}});
+  const QuicIntervalSet<int> expected_result1({{0, 1}, {9, 10}});
+
+  QuicIntervalSet<int> result1 = x1;
+  result1.Intersection(y1);
+  EXPECT_EQ(result1, expected_result1);
+
+  const QuicIntervalSet<int16_t> x2({{0, 10}, {20, 30}, {40, 90}});
+  const QuicIntervalSet<int16_t> y2(
+      {{-50, -40}, {-2, 2}, {9, 21}, {32, 41}, {85, 95}});
+  const QuicIntervalSet<int16_t> expected_result2(
+      {{0, 2}, {9, 10}, {20, 21}, {40, 41}, {85, 90}});
+
+  QuicIntervalSet<int16_t> result2 = x2;
+  result2.Intersection(y2);
+  EXPECT_EQ(result2, expected_result2);
+
+  const QuicIntervalSet<int64_t> x3({{-1, 5}, {5, 10}});
+  const QuicIntervalSet<int64_t> y3({{-10, 3}, {4, 95}});
+  const QuicIntervalSet<int64_t> expected_result3({{-1, 3}, {4, 10}});
+
+  QuicIntervalSet<int64_t> result3 = x3;
+  result3.Intersection(y3);
+  EXPECT_EQ(result3, expected_result3);
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionIdentical) {
+  QuicIntervalSet<int> copy(is);
+  EXPECT_TRUE(copy.Intersects(is));
+  EXPECT_TRUE(is.Intersects(copy));
+  is.Intersection(copy);
+  EXPECT_EQ(copy, is);
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionSuperset) {
+  QuicIntervalSet<int> mine(-1, 10000);
+  EXPECT_TRUE(mine.Intersects(is));
+  EXPECT_TRUE(is.Intersects(mine));
+  mine.Intersection(is);
+  EXPECT_EQ(is, mine);
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionSubset) {
+  QuicIntervalSet<int> copy(is);
+  QuicIntervalSet<int> theirs(-1, 10000);
+  EXPECT_TRUE(copy.Intersects(theirs));
+  EXPECT_TRUE(theirs.Intersects(copy));
+  is.Intersection(theirs);
+  EXPECT_EQ(copy, is);
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionLargeSet) {
+  QuicIntervalSet<int> mine, theirs;
+  // mine: [0, 9), [10, 19), ..., [990, 999)
+  for (int i = 0; i < 1000; i += 10) {
+    mine.Add(i, i + 9);
+  }
+
+  theirs.Add(500, 520);
+  theirs.Add(535, 545);
+  theirs.Add(801, 809);
+  EXPECT_TRUE(mine.Intersects(theirs));
+  EXPECT_TRUE(theirs.Intersects(mine));
+  mine.Intersection(theirs);
+  EXPECT_TRUE(Check(mine, 5, 500, 509, 510, 519, 535, 539, 540, 545, 801, 809));
+  EXPECT_TRUE(mine.Intersects(theirs));
+  EXPECT_TRUE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetDifference) {
+  is.Difference(other);
+  EXPECT_TRUE(Check(is, 10, 100, 200, 300, 350, 360, 370, 380, 400, 530, 600,
+                    700, 770, 900, 1000, 1100, 1200, 1900, 2000, 2100, 2200));
+  QuicIntervalSet<int> copy = is;
+  is.Difference(copy);
+  EXPECT_TRUE(is.Empty());
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceSingleBounds) {
+  std::vector<QuicInterval<int>> ivals(other.begin(), other.end());
+  for (const QuicInterval<int>& ival : ivals) {
+    is.Difference(ival.min(), ival.max());
+  }
+  EXPECT_TRUE(Check(is, 10, 100, 200, 300, 350, 360, 370, 380, 400, 530, 600,
+                    700, 770, 900, 1000, 1100, 1200, 1900, 2000, 2100, 2200));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceSingleInterval) {
+  std::vector<QuicInterval<int>> ivals(other.begin(), other.end());
+  for (const QuicInterval<int>& ival : ivals) {
+    is.Difference(ival);
+  }
+  EXPECT_TRUE(Check(is, 10, 100, 200, 300, 350, 360, 370, 380, 400, 530, 600,
+                    700, 770, 900, 1000, 1100, 1200, 1900, 2000, 2100, 2200));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceAlternatingIntervals) {
+  QuicIntervalSet<int> mine, theirs;
+  mine.Add(10, 20);
+  mine.Add(40, 50);
+  mine.Add(60, 70);
+  theirs.Add(25, 39);
+  theirs.Add(55, 59);
+  theirs.Add(75, 79);
+
+  mine.Difference(theirs);
+  EXPECT_TRUE(Check(mine, 3, 10, 20, 40, 50, 60, 70));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceEmptyMine) {
+  QuicIntervalSet<string> mine, theirs;
+  theirs.Add("a", "b");
+
+  mine.Difference(theirs);
+  EXPECT_TRUE(mine.Empty());
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceEmptyTheirs) {
+  QuicIntervalSet<string> mine, theirs;
+  mine.Add("a", "b");
+
+  mine.Difference(theirs);
+  EXPECT_EQ(1, mine.Size());
+  EXPECT_EQ("a", mine.begin()->min());
+  EXPECT_EQ("b", mine.begin()->max());
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceTheirsBeforeMine) {
+  QuicIntervalSet<string> mine, theirs;
+  mine.Add("y", "z");
+  theirs.Add("a", "b");
+
+  mine.Difference(theirs);
+  EXPECT_EQ(1, mine.Size());
+  EXPECT_EQ("y", mine.begin()->min());
+  EXPECT_EQ("z", mine.begin()->max());
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceMineBeforeTheirs) {
+  QuicIntervalSet<string> mine, theirs;
+  mine.Add("a", "b");
+  theirs.Add("y", "z");
+
+  mine.Difference(theirs);
+  EXPECT_EQ(1, mine.Size());
+  EXPECT_EQ("a", mine.begin()->min());
+  EXPECT_EQ("b", mine.begin()->max());
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceIdentical) {
+  QuicIntervalSet<string> mine;
+  mine.Add("a", "b");
+  mine.Add("c", "d");
+  QuicIntervalSet<string> theirs(mine);
+
+  mine.Difference(theirs);
+  EXPECT_TRUE(mine.Empty());
+}
+
+TEST_F(QuicIntervalSetTest, EmptyComplement) {
+  // The complement of an empty set is the input interval:
+  QuicIntervalSet<int> iset;
+  iset.Complement(100, 200);
+  EXPECT_TRUE(Check(iset, 1, 100, 200));
+}
+
+TEST(QuicIntervalSetMultipleCompactionTest, OuterCovering) {
+  QuicIntervalSet<int> iset;
+  // First add a bunch of disjoint ranges
+  iset.Add(100, 150);
+  iset.Add(200, 250);
+  iset.Add(300, 350);
+  iset.Add(400, 450);
+  EXPECT_TRUE(Check(iset, 4, 100, 150, 200, 250, 300, 350, 400, 450));
+  // Now add a big range that covers all of these ranges
+  iset.Add(0, 500);
+  EXPECT_TRUE(Check(iset, 1, 0, 500));
+}
+
+TEST(QuicIntervalSetMultipleCompactionTest, InnerCovering) {
+  QuicIntervalSet<int> iset;
+  // First add a bunch of disjoint ranges
+  iset.Add(100, 150);
+  iset.Add(200, 250);
+  iset.Add(300, 350);
+  iset.Add(400, 450);
+  EXPECT_TRUE(Check(iset, 4, 100, 150, 200, 250, 300, 350, 400, 450));
+  // Now add a big range that partially covers the left and right most ranges.
+  iset.Add(125, 425);
+  EXPECT_TRUE(Check(iset, 1, 100, 450));
+}
+
+TEST(QuicIntervalSetMultipleCompactionTest, LeftCovering) {
+  QuicIntervalSet<int> iset;
+  // First add a bunch of disjoint ranges
+  iset.Add(100, 150);
+  iset.Add(200, 250);
+  iset.Add(300, 350);
+  iset.Add(400, 450);
+  EXPECT_TRUE(Check(iset, 4, 100, 150, 200, 250, 300, 350, 400, 450));
+  // Now add a big range that partially covers the left most range.
+  iset.Add(125, 500);
+  EXPECT_TRUE(Check(iset, 1, 100, 500));
+}
+
+TEST(QuicIntervalSetMultipleCompactionTest, RightCovering) {
+  QuicIntervalSet<int> iset;
+  // First add a bunch of disjoint ranges
+  iset.Add(100, 150);
+  iset.Add(200, 250);
+  iset.Add(300, 350);
+  iset.Add(400, 450);
+  EXPECT_TRUE(Check(iset, 4, 100, 150, 200, 250, 300, 350, 400, 450));
+  // Now add a big range that partially covers the right most range.
+  iset.Add(0, 425);
+  EXPECT_TRUE(Check(iset, 1, 0, 450));
+}
+
+// Helper method for testing and verifying the results of a one-interval
+// completement case.
+static bool CheckOneComplement(int add_min,
+                               int add_max,
+                               int comp_min,
+                               int comp_max,
+                               int count,
+                               ...) {
+  QuicIntervalSet<int> iset;
+  iset.Add(add_min, add_max);
+  iset.Complement(comp_min, comp_max);
+  bool result = true;
+  va_list ap;
+  va_start(ap, count);
+  if (!VA_Check(iset, count, ap)) {
+    result = false;
+  }
+  va_end(ap);
+  return result;
+}
+
+TEST_F(QuicIntervalSetTest, SingleIntervalComplement) {
+  // Verify the complement of a set with one interval (i):
+  //                     |-----   i  -----|
+  // |----- args -----|
+  EXPECT_TRUE(CheckOneComplement(0, 10, 50, 150, 1, 50, 150));
+
+  //          |-----   i  -----|
+  //    |----- args -----|
+  EXPECT_TRUE(CheckOneComplement(50, 150, 0, 100, 1, 0, 50));
+
+  //    |-----   i  -----|
+  //    |----- args -----|
+  EXPECT_TRUE(CheckOneComplement(50, 150, 50, 150, 0));
+
+  //    |----------   i  ----------|
+  //        |----- args -----|
+  EXPECT_TRUE(CheckOneComplement(50, 500, 100, 300, 0));
+
+  //        |----- i -----|
+  //    |---------- args  ----------|
+  EXPECT_TRUE(CheckOneComplement(50, 500, 0, 800, 2, 0, 50, 500, 800));
+
+  //    |-----   i  -----|
+  //          |----- args -----|
+  EXPECT_TRUE(CheckOneComplement(50, 150, 100, 300, 1, 150, 300));
+
+  //    |-----   i  -----|
+  //                        |----- args -----|
+  EXPECT_TRUE(CheckOneComplement(50, 150, 200, 300, 1, 200, 300));
+}
+
+// Helper method that copies <iset> and takes its complement,
+// returning false if Check succeeds.
+static bool CheckComplement(const QuicIntervalSet<int>& iset,
+                            int comp_min,
+                            int comp_max,
+                            int count,
+                            ...) {
+  QuicIntervalSet<int> iset_copy = iset;
+  iset_copy.Complement(comp_min, comp_max);
+  bool result = true;
+  va_list ap;
+  va_start(ap, count);
+  if (!VA_Check(iset_copy, count, ap)) {
+    result = false;
+  }
+  va_end(ap);
+  return result;
+}
+
+TEST_F(QuicIntervalSetTest, MultiIntervalComplement) {
+  // Initialize a small test set:
+  QuicIntervalSet<int> iset;
+  iset.Add(100, 200);
+  iset.Add(300, 400);
+  iset.Add(500, 600);
+
+  //                     |-----   i  -----|
+  // |----- comp -----|
+  EXPECT_TRUE(CheckComplement(iset, 0, 50, 1, 0, 50));
+
+  //          |-----   i  -----|
+  //    |----- comp -----|
+  EXPECT_TRUE(CheckComplement(iset, 0, 200, 1, 0, 100));
+  EXPECT_TRUE(CheckComplement(iset, 0, 220, 2, 0, 100, 200, 220));
+
+  //    |-----   i  -----|
+  //    |----- comp -----|
+  EXPECT_TRUE(CheckComplement(iset, 100, 600, 2, 200, 300, 400, 500));
+
+  //    |----------   i  ----------|
+  //        |----- comp -----|
+  EXPECT_TRUE(CheckComplement(iset, 300, 400, 0));
+  EXPECT_TRUE(CheckComplement(iset, 250, 400, 1, 250, 300));
+  EXPECT_TRUE(CheckComplement(iset, 300, 450, 1, 400, 450));
+  EXPECT_TRUE(CheckComplement(iset, 250, 450, 2, 250, 300, 400, 450));
+
+  //        |----- i -----|
+  //    |---------- comp  ----------|
+  EXPECT_TRUE(
+      CheckComplement(iset, 0, 700, 4, 0, 100, 200, 300, 400, 500, 600, 700));
+
+  //    |-----   i  -----|
+  //          |----- comp -----|
+  EXPECT_TRUE(CheckComplement(iset, 400, 700, 2, 400, 500, 600, 700));
+  EXPECT_TRUE(CheckComplement(iset, 350, 700, 2, 400, 500, 600, 700));
+
+  //    |-----   i  -----|
+  //                        |----- comp -----|
+  EXPECT_TRUE(CheckComplement(iset, 700, 800, 1, 700, 800));
+}
+
+// Verifies ToString, operator<< don't assert.
+TEST_F(QuicIntervalSetTest, ToString) {
+  QuicIntervalSet<int> iset;
+  iset.Add(300, 400);
+  iset.Add(100, 200);
+  iset.Add(500, 600);
+  EXPECT_TRUE(!iset.ToString().empty());
+  QUIC_VLOG(2) << iset;
+  // Order and format of ToString() output is guaranteed.
+  EXPECT_EQ("{ [100, 200) [300, 400) [500, 600) }", iset.ToString());
+  EXPECT_EQ("{ [1, 2) }", QuicIntervalSet<int>(1, 2).ToString());
+  EXPECT_EQ("{ }", QuicIntervalSet<int>().ToString());
+}
+
+TEST_F(QuicIntervalSetTest, ConstructionDiscardsEmptyInterval) {
+  EXPECT_TRUE(QuicIntervalSet<int>(QuicInterval<int>(2, 2)).Empty());
+  EXPECT_TRUE(QuicIntervalSet<int>(2, 2).Empty());
+  EXPECT_FALSE(QuicIntervalSet<int>(QuicInterval<int>(2, 3)).Empty());
+  EXPECT_FALSE(QuicIntervalSet<int>(2, 3).Empty());
+}
+
+TEST_F(QuicIntervalSetTest, Swap) {
+  QuicIntervalSet<int> a, b;
+  a.Add(300, 400);
+  b.Add(100, 200);
+  b.Add(500, 600);
+  a.Swap(&b);
+  EXPECT_TRUE(Check(a, 2, 100, 200, 500, 600));
+  EXPECT_TRUE(Check(b, 1, 300, 400));
+  swap(a, b);
+  EXPECT_TRUE(Check(a, 1, 300, 400));
+  EXPECT_TRUE(Check(b, 2, 100, 200, 500, 600));
+}
+
+TEST_F(QuicIntervalSetTest, OutputReturnsOstreamRef) {
+  std::stringstream ss;
+  const QuicIntervalSet<int> v(QuicInterval<int>(1, 2));
+  auto return_type_is_a_ref = [](std::ostream&) {};
+  return_type_is_a_ref(ss << v);
+}
+
+struct NotOstreamable {
+  bool operator<(const NotOstreamable& other) const { return false; }
+  bool operator>(const NotOstreamable& other) const { return false; }
+  bool operator!=(const NotOstreamable& other) const { return false; }
+  bool operator>=(const NotOstreamable& other) const { return true; }
+  bool operator<=(const NotOstreamable& other) const { return true; }
+  bool operator==(const NotOstreamable& other) const { return true; }
+};
+
+TEST_F(QuicIntervalSetTest, IntervalOfTypeWithNoOstreamSupport) {
+  const NotOstreamable v;
+  const QuicIntervalSet<NotOstreamable> d(QuicInterval<NotOstreamable>(v, v));
+  // EXPECT_EQ builds a string representation of d. If d::operator<<()
+  // would be defined then this test would not compile because NotOstreamable
+  // objects lack the operator<<() support.
+  EXPECT_EQ(d, d);
+}
+
+class QuicIntervalSetInitTest : public QuicTest {
+ protected:
+  const std::vector<QuicInterval<int>> intervals_{{0, 1}, {2, 4}};
+};
+
+TEST_F(QuicIntervalSetInitTest, DirectInit) {
+  std::initializer_list<QuicInterval<int>> il = {{0, 1}, {2, 3}, {3, 4}};
+  QuicIntervalSet<int> s(il);
+  EXPECT_THAT(s, ElementsAreArray(intervals_));
+}
+
+TEST_F(QuicIntervalSetInitTest, CopyInit) {
+  std::initializer_list<QuicInterval<int>> il = {{0, 1}, {2, 3}, {3, 4}};
+  QuicIntervalSet<int> s = il;
+  EXPECT_THAT(s, ElementsAreArray(intervals_));
+}
+
+TEST_F(QuicIntervalSetInitTest, AssignIterPair) {
+  QuicIntervalSet<int> s(0, 1000);  // Make sure assign clears.
+  s.assign(intervals_.begin(), intervals_.end());
+  EXPECT_THAT(s, ElementsAreArray(intervals_));
+}
+
+TEST_F(QuicIntervalSetInitTest, AssignInitList) {
+  QuicIntervalSet<int> s(0, 1000);  // Make sure assign clears.
+  s.assign({{0, 1}, {2, 3}, {3, 4}});
+  EXPECT_THAT(s, ElementsAreArray(intervals_));
+}
+
+TEST_F(QuicIntervalSetInitTest, AssignmentInitList) {
+  std::initializer_list<QuicInterval<int>> il = {{0, 1}, {2, 3}, {3, 4}};
+  QuicIntervalSet<int> s;
+  s = il;
+  EXPECT_THAT(s, ElementsAreArray(intervals_));
+}
+
+TEST_F(QuicIntervalSetInitTest, BracedInitThenBracedAssign) {
+  QuicIntervalSet<int> s{{0, 1}, {2, 3}, {3, 4}};
+  s = {{0, 1}, {2, 4}};
+  EXPECT_THAT(s, ElementsAreArray(intervals_));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace quic
diff --git a/quic/core/quic_interval_test.cc b/quic/core/quic_interval_test.cc
new file mode 100644
index 0000000..3cb134e
--- /dev/null
+++ b/quic/core/quic_interval_test.cc
@@ -0,0 +1,457 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
+
+#include <sstream>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+template <typename ForwardIterator>
+void STLDeleteContainerPointers(ForwardIterator begin, ForwardIterator end) {
+  while (begin != end) {
+    auto temp = begin;
+    ++begin;
+    delete *temp;
+  }
+}
+
+template <typename T>
+void STLDeleteElements(T* container) {
+  if (!container)
+    return;
+  STLDeleteContainerPointers(container->begin(), container->end());
+  container->clear();
+}
+
+class ConstructorListener {
+ public:
+  ConstructorListener(int* copy_construct_counter, int* move_construct_counter)
+      : copy_construct_counter_(copy_construct_counter),
+        move_construct_counter_(move_construct_counter) {
+    *copy_construct_counter_ = 0;
+    *move_construct_counter_ = 0;
+  }
+  ConstructorListener(const ConstructorListener& other) {
+    copy_construct_counter_ = other.copy_construct_counter_;
+    move_construct_counter_ = other.move_construct_counter_;
+    ++*copy_construct_counter_;
+  }
+  ConstructorListener(ConstructorListener&& other) {
+    copy_construct_counter_ = other.copy_construct_counter_;
+    move_construct_counter_ = other.move_construct_counter_;
+    ++*move_construct_counter_;
+  }
+  bool operator<(const ConstructorListener&) { return false; }
+  bool operator>(const ConstructorListener&) { return false; }
+  bool operator<=(const ConstructorListener&) { return true; }
+  bool operator>=(const ConstructorListener&) { return true; }
+  bool operator==(const ConstructorListener&) { return true; }
+
+ private:
+  int* copy_construct_counter_;
+  int* move_construct_counter_;
+};
+
+TEST(IntervalConstructorTest, Move) {
+  int object1_copy_count, object1_move_count;
+  ConstructorListener object1(&object1_copy_count, &object1_move_count);
+  int object2_copy_count, object2_move_count;
+  ConstructorListener object2(&object2_copy_count, &object2_move_count);
+
+  QuicInterval<ConstructorListener> interval(object1, std::move(object2));
+  EXPECT_EQ(1, object1_copy_count);
+  EXPECT_EQ(0, object1_move_count);
+  EXPECT_EQ(0, object2_copy_count);
+  EXPECT_EQ(1, object2_move_count);
+}
+
+TEST(IntervalConstructorTest, ImplicitConversion) {
+  struct WrappedInt {
+    WrappedInt(int value) : value(value) {}
+    bool operator<(const WrappedInt& other) { return value < other.value; }
+    bool operator>(const WrappedInt& other) { return value > other.value; }
+    bool operator<=(const WrappedInt& other) { return value <= other.value; }
+    bool operator>=(const WrappedInt& other) { return value >= other.value; }
+    bool operator==(const WrappedInt& other) { return value == other.value; }
+    int value;
+  };
+
+  static_assert(std::is_convertible<int, WrappedInt>::value, "");
+  static_assert(
+      std::is_constructible<QuicInterval<WrappedInt>, int, int>::value, "");
+
+  QuicInterval<WrappedInt> i(10, 20);
+  EXPECT_EQ(10, i.min().value);
+  EXPECT_EQ(20, i.max().value);
+}
+
+class IntervalTest : public QuicTest {
+ protected:
+  // Test intersection between the two intervals i1 and i2.  Tries
+  // i1.IntersectWith(i2) and vice versa. The intersection should change i1 iff
+  // changes_i1 is true, and the same for changes_i2.  The resulting
+  // intersection should be result.
+  void TestIntersect(const QuicInterval<int64_t>& i1,
+                     const QuicInterval<int64_t>& i2,
+                     bool changes_i1,
+                     bool changes_i2,
+                     const QuicInterval<int64_t>& result) {
+    QuicInterval<int64_t> i;
+    i = i1;
+    EXPECT_TRUE(i.IntersectWith(i2) == changes_i1 && i == result);
+    i = i2;
+    EXPECT_TRUE(i.IntersectWith(i1) == changes_i2 && i == result);
+  }
+};
+
+TEST_F(IntervalTest, ConstructorsCopyAndClear) {
+  QuicInterval<int32_t> empty;
+  EXPECT_TRUE(empty.Empty());
+
+  QuicInterval<int32_t> d2(0, 100);
+  EXPECT_EQ(0, d2.min());
+  EXPECT_EQ(100, d2.max());
+  EXPECT_EQ(QuicInterval<int32_t>(0, 100), d2);
+  EXPECT_NE(QuicInterval<int32_t>(0, 99), d2);
+
+  empty = d2;
+  EXPECT_EQ(0, d2.min());
+  EXPECT_EQ(100, d2.max());
+  EXPECT_TRUE(empty == d2);
+  EXPECT_EQ(empty, d2);
+  EXPECT_TRUE(d2 == empty);
+  EXPECT_EQ(d2, empty);
+
+  QuicInterval<int32_t> max_less_than_min(40, 20);
+  EXPECT_TRUE(max_less_than_min.Empty());
+  EXPECT_EQ(40, max_less_than_min.min());
+  EXPECT_EQ(20, max_less_than_min.max());
+
+  QuicInterval<int> d3(10, 20);
+  d3.Clear();
+  EXPECT_TRUE(d3.Empty());
+}
+
+TEST_F(IntervalTest, MakeQuicInterval) {
+  static_assert(
+      std::is_same<QuicInterval<int>, decltype(MakeQuicInterval(0, 3))>::value,
+      "Type is deduced incorrectly.");
+  static_assert(std::is_same<QuicInterval<double>,
+                             decltype(MakeQuicInterval(0., 3.))>::value,
+                "Type is deduced incorrectly.");
+
+  EXPECT_EQ(MakeQuicInterval(0., 3.), QuicInterval<double>(0, 3));
+}
+
+TEST_F(IntervalTest, GettersSetters) {
+  QuicInterval<int32_t> d1(100, 200);
+
+  // SetMin:
+  d1.SetMin(30);
+  EXPECT_EQ(30, d1.min());
+  EXPECT_EQ(200, d1.max());
+
+  // SetMax:
+  d1.SetMax(220);
+  EXPECT_EQ(30, d1.min());
+  EXPECT_EQ(220, d1.max());
+
+  // Set:
+  d1.Clear();
+  d1.Set(30, 220);
+  EXPECT_EQ(30, d1.min());
+  EXPECT_EQ(220, d1.max());
+
+  // SpanningUnion:
+  QuicInterval<int32_t> d2;
+  EXPECT_TRUE(!d1.SpanningUnion(d2));
+  EXPECT_EQ(30, d1.min());
+  EXPECT_EQ(220, d1.max());
+
+  EXPECT_TRUE(d2.SpanningUnion(d1));
+  EXPECT_EQ(30, d2.min());
+  EXPECT_EQ(220, d2.max());
+
+  d2.SetMin(40);
+  d2.SetMax(100);
+  EXPECT_TRUE(!d1.SpanningUnion(d2));
+  EXPECT_EQ(30, d1.min());
+  EXPECT_EQ(220, d1.max());
+
+  d2.SetMin(20);
+  d2.SetMax(100);
+  EXPECT_TRUE(d1.SpanningUnion(d2));
+  EXPECT_EQ(20, d1.min());
+  EXPECT_EQ(220, d1.max());
+
+  d2.SetMin(50);
+  d2.SetMax(300);
+  EXPECT_TRUE(d1.SpanningUnion(d2));
+  EXPECT_EQ(20, d1.min());
+  EXPECT_EQ(300, d1.max());
+
+  d2.SetMin(0);
+  d2.SetMax(500);
+  EXPECT_TRUE(d1.SpanningUnion(d2));
+  EXPECT_EQ(0, d1.min());
+  EXPECT_EQ(500, d1.max());
+
+  d2.SetMin(100);
+  d2.SetMax(0);
+  EXPECT_TRUE(!d1.SpanningUnion(d2));
+  EXPECT_EQ(0, d1.min());
+  EXPECT_EQ(500, d1.max());
+  EXPECT_TRUE(d2.SpanningUnion(d1));
+  EXPECT_EQ(0, d2.min());
+  EXPECT_EQ(500, d2.max());
+}
+
+TEST_F(IntervalTest, CoveringOps) {
+  const QuicInterval<int64_t> empty;
+  const QuicInterval<int64_t> d(100, 200);
+  const QuicInterval<int64_t> d1(0, 50);
+  const QuicInterval<int64_t> d2(50, 110);
+  const QuicInterval<int64_t> d3(110, 180);
+  const QuicInterval<int64_t> d4(180, 220);
+  const QuicInterval<int64_t> d5(220, 300);
+  const QuicInterval<int64_t> d6(100, 150);
+  const QuicInterval<int64_t> d7(150, 200);
+  const QuicInterval<int64_t> d8(0, 300);
+
+  // Intersection:
+  EXPECT_TRUE(d.Intersects(d));
+  EXPECT_TRUE(!empty.Intersects(d) && !d.Intersects(empty));
+  EXPECT_TRUE(!d.Intersects(d1) && !d1.Intersects(d));
+  EXPECT_TRUE(d.Intersects(d2) && d2.Intersects(d));
+  EXPECT_TRUE(d.Intersects(d3) && d3.Intersects(d));
+  EXPECT_TRUE(d.Intersects(d4) && d4.Intersects(d));
+  EXPECT_TRUE(!d.Intersects(d5) && !d5.Intersects(d));
+  EXPECT_TRUE(d.Intersects(d6) && d6.Intersects(d));
+  EXPECT_TRUE(d.Intersects(d7) && d7.Intersects(d));
+  EXPECT_TRUE(d.Intersects(d8) && d8.Intersects(d));
+
+  QuicInterval<int64_t> i;
+  EXPECT_TRUE(d.Intersects(d, &i) && d == i);
+  EXPECT_TRUE(!empty.Intersects(d, nullptr) && !d.Intersects(empty, nullptr));
+  EXPECT_TRUE(!d.Intersects(d1, nullptr) && !d1.Intersects(d, nullptr));
+  EXPECT_TRUE(d.Intersects(d2, &i) && i == QuicInterval<int64_t>(100, 110));
+  EXPECT_TRUE(d2.Intersects(d, &i) && i == QuicInterval<int64_t>(100, 110));
+  EXPECT_TRUE(d.Intersects(d3, &i) && i == d3);
+  EXPECT_TRUE(d3.Intersects(d, &i) && i == d3);
+  EXPECT_TRUE(d.Intersects(d4, &i) && i == QuicInterval<int64_t>(180, 200));
+  EXPECT_TRUE(d4.Intersects(d, &i) && i == QuicInterval<int64_t>(180, 200));
+  EXPECT_TRUE(!d.Intersects(d5, nullptr) && !d5.Intersects(d, nullptr));
+  EXPECT_TRUE(d.Intersects(d6, &i) && i == d6);
+  EXPECT_TRUE(d6.Intersects(d, &i) && i == d6);
+  EXPECT_TRUE(d.Intersects(d7, &i) && i == d7);
+  EXPECT_TRUE(d7.Intersects(d, &i) && i == d7);
+  EXPECT_TRUE(d.Intersects(d8, &i) && i == d);
+  EXPECT_TRUE(d8.Intersects(d, &i) && i == d);
+
+  // Test IntersectsWith().
+  // Arguments are TestIntersect(i1, i2, changes_i1, changes_i2, result).
+  TestIntersect(empty, d, false, true, empty);
+  TestIntersect(d, d1, true, true, empty);
+  TestIntersect(d1, d2, true, true, empty);
+  TestIntersect(d, d2, true, true, QuicInterval<int64_t>(100, 110));
+  TestIntersect(d8, d, true, false, d);
+  TestIntersect(d8, d1, true, false, d1);
+  TestIntersect(d8, d5, true, false, d5);
+
+  // Contains:
+  EXPECT_TRUE(!empty.Contains(d) && !d.Contains(empty));
+  EXPECT_TRUE(d.Contains(d));
+  EXPECT_TRUE(!d.Contains(d1) && !d1.Contains(d));
+  EXPECT_TRUE(!d.Contains(d2) && !d2.Contains(d));
+  EXPECT_TRUE(d.Contains(d3) && !d3.Contains(d));
+  EXPECT_TRUE(!d.Contains(d4) && !d4.Contains(d));
+  EXPECT_TRUE(!d.Contains(d5) && !d5.Contains(d));
+  EXPECT_TRUE(d.Contains(d6) && !d6.Contains(d));
+  EXPECT_TRUE(d.Contains(d7) && !d7.Contains(d));
+  EXPECT_TRUE(!d.Contains(d8) && d8.Contains(d));
+
+  EXPECT_TRUE(d.Contains(100));
+  EXPECT_TRUE(!d.Contains(200));
+  EXPECT_TRUE(d.Contains(150));
+  EXPECT_TRUE(!d.Contains(99));
+  EXPECT_TRUE(!d.Contains(201));
+
+  // Difference:
+  std::vector<QuicInterval<int64_t>*> diff;
+
+  EXPECT_TRUE(!d.Difference(empty, &diff));
+  EXPECT_EQ(1, diff.size());
+  EXPECT_EQ(100, diff[0]->min());
+  EXPECT_EQ(200, diff[0]->max());
+  STLDeleteElements(&diff);
+  EXPECT_TRUE(!empty.Difference(d, &diff) && diff.empty());
+
+  EXPECT_TRUE(d.Difference(d, &diff) && diff.empty());
+  EXPECT_TRUE(!d.Difference(d1, &diff));
+  EXPECT_EQ(1, diff.size());
+  EXPECT_EQ(100, diff[0]->min());
+  EXPECT_EQ(200, diff[0]->max());
+  STLDeleteElements(&diff);
+
+  QuicInterval<int64_t> lo;
+  QuicInterval<int64_t> hi;
+
+  EXPECT_TRUE(d.Difference(d2, &lo, &hi));
+  EXPECT_TRUE(lo.Empty());
+  EXPECT_EQ(110, hi.min());
+  EXPECT_EQ(200, hi.max());
+  EXPECT_TRUE(d.Difference(d2, &diff));
+  EXPECT_EQ(1, diff.size());
+  EXPECT_EQ(110, diff[0]->min());
+  EXPECT_EQ(200, diff[0]->max());
+  STLDeleteElements(&diff);
+
+  EXPECT_TRUE(d.Difference(d3, &lo, &hi));
+  EXPECT_EQ(100, lo.min());
+  EXPECT_EQ(110, lo.max());
+  EXPECT_EQ(180, hi.min());
+  EXPECT_EQ(200, hi.max());
+  EXPECT_TRUE(d.Difference(d3, &diff));
+  EXPECT_EQ(2, diff.size());
+  EXPECT_EQ(100, diff[0]->min());
+  EXPECT_EQ(110, diff[0]->max());
+  EXPECT_EQ(180, diff[1]->min());
+  EXPECT_EQ(200, diff[1]->max());
+  STLDeleteElements(&diff);
+
+  EXPECT_TRUE(d.Difference(d4, &lo, &hi));
+  EXPECT_EQ(100, lo.min());
+  EXPECT_EQ(180, lo.max());
+  EXPECT_TRUE(hi.Empty());
+  EXPECT_TRUE(d.Difference(d4, &diff));
+  EXPECT_EQ(1, diff.size());
+  EXPECT_EQ(100, diff[0]->min());
+  EXPECT_EQ(180, diff[0]->max());
+  STLDeleteElements(&diff);
+
+  EXPECT_FALSE(d.Difference(d5, &lo, &hi));
+  EXPECT_EQ(100, lo.min());
+  EXPECT_EQ(200, lo.max());
+  EXPECT_TRUE(hi.Empty());
+  EXPECT_FALSE(d.Difference(d5, &diff));
+  EXPECT_EQ(1, diff.size());
+  EXPECT_EQ(100, diff[0]->min());
+  EXPECT_EQ(200, diff[0]->max());
+  STLDeleteElements(&diff);
+
+  EXPECT_TRUE(d.Difference(d6, &lo, &hi));
+  EXPECT_TRUE(lo.Empty());
+  EXPECT_EQ(150, hi.min());
+  EXPECT_EQ(200, hi.max());
+  EXPECT_TRUE(d.Difference(d6, &diff));
+  EXPECT_EQ(1, diff.size());
+  EXPECT_EQ(150, diff[0]->min());
+  EXPECT_EQ(200, diff[0]->max());
+  STLDeleteElements(&diff);
+
+  EXPECT_TRUE(d.Difference(d7, &lo, &hi));
+  EXPECT_EQ(100, lo.min());
+  EXPECT_EQ(150, lo.max());
+  EXPECT_TRUE(hi.Empty());
+  EXPECT_TRUE(d.Difference(d7, &diff));
+  EXPECT_EQ(1, diff.size());
+  EXPECT_EQ(100, diff[0]->min());
+  EXPECT_EQ(150, diff[0]->max());
+  STLDeleteElements(&diff);
+
+  EXPECT_TRUE(d.Difference(d8, &lo, &hi));
+  EXPECT_TRUE(lo.Empty());
+  EXPECT_TRUE(hi.Empty());
+  EXPECT_TRUE(d.Difference(d8, &diff) && diff.empty());
+}
+
+TEST_F(IntervalTest, Length) {
+  const QuicInterval<int> empty1;
+  const QuicInterval<int> empty2(1, 1);
+  const QuicInterval<int> empty3(1, 0);
+  const QuicInterval<QuicTime> empty4(
+      QuicTime::Zero() + QuicTime::Delta::FromSeconds(1), QuicTime::Zero());
+  const QuicInterval<int> d1(1, 2);
+  const QuicInterval<int> d2(0, 50);
+  const QuicInterval<QuicTime> d3(
+      QuicTime::Zero(), QuicTime::Zero() + QuicTime::Delta::FromSeconds(1));
+  const QuicInterval<QuicTime> d4(
+      QuicTime::Zero() + QuicTime::Delta::FromSeconds(3600),
+      QuicTime::Zero() + QuicTime::Delta::FromSeconds(5400));
+
+  EXPECT_EQ(0, empty1.Length());
+  EXPECT_EQ(0, empty2.Length());
+  EXPECT_EQ(0, empty3.Length());
+  EXPECT_EQ(QuicTime::Delta::Zero(), empty4.Length());
+  EXPECT_EQ(1, d1.Length());
+  EXPECT_EQ(50, d2.Length());
+  EXPECT_EQ(QuicTime::Delta::FromSeconds(1), d3.Length());
+  EXPECT_EQ(QuicTime::Delta::FromSeconds(1800), d4.Length());
+}
+
+TEST_F(IntervalTest, IntervalOfTypeWithNoOperatorMinus) {
+  // QuicInterval<T> should work even if T does not support operator-().  We
+  // just can't call QuicInterval<T>::Length() for such types.
+  const QuicInterval<string> d1("a", "b");
+  const QuicInterval<std::pair<int, int>> d2({1, 2}, {4, 3});
+  EXPECT_EQ("a", d1.min());
+  EXPECT_EQ("b", d1.max());
+  EXPECT_EQ(std::make_pair(1, 2), d2.min());
+  EXPECT_EQ(std::make_pair(4, 3), d2.max());
+}
+
+struct NoEquals {
+  NoEquals(int v) : value(v) {}  // NOLINT
+  int value;
+  bool operator<(const NoEquals& other) const { return value < other.value; }
+};
+
+TEST_F(IntervalTest, OrderedComparisonForTypeWithoutEquals) {
+  const QuicInterval<NoEquals> d1(0, 4);
+  const QuicInterval<NoEquals> d2(0, 3);
+  const QuicInterval<NoEquals> d3(1, 4);
+  const QuicInterval<NoEquals> d4(1, 5);
+  const QuicInterval<NoEquals> d6(0, 4);
+  EXPECT_TRUE(d1 < d2);
+  EXPECT_TRUE(d1 < d3);
+  EXPECT_TRUE(d1 < d4);
+  EXPECT_FALSE(d1 < d6);
+}
+
+TEST_F(IntervalTest, OutputReturnsOstreamRef) {
+  std::stringstream ss;
+  const QuicInterval<int> v(1, 2);
+  // If (ss << v) were to return a value, it wouldn't match the signature of
+  // return_type_is_a_ref() function.
+  auto return_type_is_a_ref = [](std::ostream&) {};
+  return_type_is_a_ref(ss << v);
+}
+
+struct NotOstreamable {
+  bool operator<(const NotOstreamable& other) const { return false; }
+  bool operator>=(const NotOstreamable& other) const { return true; }
+  bool operator==(const NotOstreamable& other) const { return true; }
+};
+
+TEST_F(IntervalTest, IntervalOfTypeWithNoOstreamSupport) {
+  const NotOstreamable v;
+  const QuicInterval<NotOstreamable> d(v, v);
+  // EXPECT_EQ builds a string representation of d. If d::operator<<() would be
+  // defined then this test would not compile because NotOstreamable objects
+  // lack the operator<<() support.
+  EXPECT_EQ(d, d);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace quic
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc
index cc4f50e..6ad5351 100644
--- a/quic/core/quic_packet_creator.cc
+++ b/quic/core/quic_packet_creator.cc
@@ -21,8 +21,29 @@
 #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
 
 namespace quic {
+namespace {
+
+QuicLongHeaderType EncryptionlevelToLongHeaderType(EncryptionLevel level) {
+  switch (level) {
+    case ENCRYPTION_NONE:
+      return INITIAL;
+    case ENCRYPTION_ZERO_RTT:
+      return ZERO_RTT_PROTECTED;
+    case ENCRYPTION_FORWARD_SECURE:
+      QUIC_BUG
+          << "Try to derive long header type for packet with encryption level: "
+          << QuicUtils::EncryptionLevelToString(level);
+      return INVALID_PACKET_TYPE;
+    default:
+      QUIC_BUG << QuicUtils::EncryptionLevelToString(level);
+      return INVALID_PACKET_TYPE;
+  }
+}
+
+}  // namespace
 
 #define ENDPOINT \
   (framer_->perspective() == Perspective::IS_SERVER ? "Server: " : "Client: ")
@@ -60,7 +81,9 @@
       needs_full_padding_(false),
       can_set_transmission_type_(false),
       set_transmission_type_for_next_frame_(
-          GetQuicReloadableFlag(quic_set_transmission_type_for_next_frame)) {
+          GetQuicReloadableFlag(quic_set_transmission_type_for_next_frame)),
+      encryption_level_driven_long_header_type_(
+          GetQuicReloadableFlag(quic_encryption_driven_header_type)) {
   SetMaxPacketLength(kDefaultMaxPacketSize);
 }
 
@@ -132,6 +155,25 @@
       framer_->transport_version(), QuicPacketNumber(delta * 4));
 }
 
+bool QuicPacketCreator::ConsumeCryptoData(EncryptionLevel level,
+                                          size_t write_length,
+                                          QuicStreamOffset offset,
+                                          TransmissionType transmission_type,
+                                          QuicFrame* frame) {
+  if (!CreateCryptoFrame(level, write_length, offset, frame)) {
+    return false;
+  }
+  // When crypto data was sent in stream frames, ConsumeData is called with
+  // |needs_full_padding = true|. Keep the same behavior here when sending
+  // crypto frames.
+  //
+  // TODO(nharper): Check what the IETF drafts say about padding out initial
+  // messages and change this as appropriate.
+  needs_full_padding_ = true;
+  return AddFrame(*frame, /*save_retransmittable_frames*/ true,
+                  transmission_type);
+}
+
 bool QuicPacketCreator::ConsumeData(QuicStreamId id,
                                     size_t write_length,
                                     size_t iov_offset,
@@ -190,7 +232,7 @@
 // this information, leading to a cascade of changes and B) the
 // higher-up software does not always loop, calling
 // StreamFramePacketOverhead() once for every packet -- eg there is
-// a test in quic_connetion_test that calls it once and assumes that
+// a test in quic_connection_test that calls it once and assumes that
 // the value is the same for all packets.
 
 // static
@@ -201,13 +243,16 @@
     bool include_version,
     bool include_diversification_nonce,
     QuicPacketNumberLength packet_number_length,
+    QuicVariableLengthIntegerLength retry_token_length_length,
+    QuicVariableLengthIntegerLength length_length,
     QuicStreamOffset offset) {
   return GetPacketHeaderSize(version, destination_connection_id_length,
                              source_connection_id_length, include_version,
                              include_diversification_nonce,
-                             packet_number_length) +
+                             packet_number_length, retry_token_length_length, 0,
+                             length_length) +
 
-         // Assumes this is packet with a aingle stream frame in it. Since
+         // Assumes this is a packet with a single stream frame in it. Since
          // last_frame_in_packet is set true, the size of the length field is
          // not included in the calculation. This is OK because in other places
          // in the code, the logic adds back 2 (the size of the Google QUIC
@@ -236,7 +281,8 @@
       StreamFramePacketOverhead(
           framer_->transport_version(), GetDestinationConnectionIdLength(),
           GetSourceConnectionIdLength(), kIncludeVersion,
-          IncludeNonceInPublicHeader(), PACKET_6BYTE_PACKET_NUMBER, offset));
+          IncludeNonceInPublicHeader(), PACKET_6BYTE_PACKET_NUMBER,
+          GetRetryTokenLengthLength(), GetLengthLength(), offset));
 
   QUIC_BUG_IF(!HasRoomForStreamFrame(id, offset, data_size))
       << "No room for Stream frame, BytesFree: " << BytesFree()
@@ -261,6 +307,21 @@
   *frame = QuicFrame(QuicStreamFrame(id, set_fin, offset, bytes_consumed));
 }
 
+bool QuicPacketCreator::CreateCryptoFrame(EncryptionLevel level,
+                                          size_t write_length,
+                                          QuicStreamOffset offset,
+                                          QuicFrame* frame) {
+  size_t min_frame_size =
+      QuicFramer::GetMinCryptoFrameSize(write_length, offset);
+  if (BytesFree() <= min_frame_size) {
+    return false;
+  }
+  size_t max_write_length = BytesFree() - min_frame_size;
+  size_t bytes_consumed = std::min<size_t>(max_write_length, write_length);
+  *frame = QuicFrame(new QuicCryptoFrame(level, offset, bytes_consumed));
+  return true;
+}
+
 void QuicPacketCreator::ReserializeAllFrames(
     const QuicPendingRetransmission& retransmission,
     char* buffer,
@@ -369,9 +430,9 @@
     encrypted_buffer = stack_buffer;
   }
 
-  QuicDataWriter writer(kMaxPacketSize, encrypted_buffer,
-                        framer_->endianness());
-  if (!framer_->AppendPacketHeader(header, &writer)) {
+  QuicDataWriter writer(kMaxPacketSize, encrypted_buffer);
+  size_t length_field_offset = 0;
+  if (!framer_->AppendPacketHeader(header, &writer, &length_field_offset)) {
     QUIC_BUG << "AppendPacketHeader failed";
     return;
   }
@@ -405,6 +466,11 @@
     return;
   }
 
+  if (!framer_->WriteIetfLongHeaderLength(header, &writer, length_field_offset,
+                                          packet_.encryption_level)) {
+    return;
+  }
+
   if (ShouldSetTransmissionTypeForNextFrame()) {
     QUIC_RELOADABLE_FLAG_COUNT_N(quic_set_transmission_type_for_next_frame, 1,
                                  2);
@@ -453,7 +519,7 @@
       !queued_frames_.empty() && queued_frames_.back().type == MESSAGE_FRAME;
   if (has_trailing_message_frame) {
     return QuicDataWriter::GetVarInt62Len(
-        queued_frames_.back().message_frame->message_data.length());
+        queued_frames_.back().message_frame->message_length);
   }
   // If the last frame in the packet is a stream frame, then it will expand to
   // include the stream_length field when a new frame is added.
@@ -482,7 +548,8 @@
   packet_size_ = GetPacketHeaderSize(
       framer_->transport_version(), GetDestinationConnectionIdLength(),
       GetSourceConnectionIdLength(), IncludeVersionInHeader(),
-      IncludeNonceInPublicHeader(), GetPacketNumberLength());
+      IncludeNonceInPublicHeader(), GetPacketNumberLength(),
+      GetRetryTokenLengthLength(), GetRetryToken().length(), GetLengthLength());
   return packet_size_;
 }
 
@@ -517,8 +584,9 @@
   DCHECK_GE(max_plaintext_size_, packet_size_);
   // Use the packet_size_ instead of the buffer size to ensure smaller
   // packet sizes are properly used.
-  size_t length = framer_->BuildDataPacket(header, queued_frames_,
-                                           encrypted_buffer, packet_size_);
+  size_t length =
+      framer_->BuildDataPacket(header, queued_frames_, encrypted_buffer,
+                               packet_size_, packet_.encryption_level);
   if (length == 0) {
     QUIC_BUG << "Failed to serialize " << queued_frames_.size() << " frames.";
     return;
@@ -573,8 +641,8 @@
   FillPacketHeader(&header);
 
   std::unique_ptr<char[]> buffer(new char[kMaxPacketSize]);
-  size_t length = framer_->BuildConnectivityProbingPacket(header, buffer.get(),
-                                                          max_plaintext_size_);
+  size_t length = framer_->BuildConnectivityProbingPacket(
+      header, buffer.get(), max_plaintext_size_, packet_.encryption_level);
   DCHECK(length);
 
   const size_t encrypted_length = framer_->EncryptInPlace(
@@ -606,7 +674,8 @@
 
   std::unique_ptr<char[]> buffer(new char[kMaxPacketSize]);
   size_t length = framer_->BuildPaddedPathChallengePacket(
-      header, buffer.get(), max_plaintext_size_, payload, random_);
+      header, buffer.get(), max_plaintext_size_, payload, random_,
+      packet_.encryption_level);
   DCHECK(length);
 
   const size_t encrypted_length = framer_->EncryptInPlace(
@@ -639,7 +708,8 @@
 
   std::unique_ptr<char[]> buffer(new char[kMaxPacketSize]);
   size_t length = framer_->BuildPathResponsePacket(
-      header, buffer.get(), max_plaintext_size_, payloads, is_padded);
+      header, buffer.get(), max_plaintext_size_, payloads, is_padded,
+      packet_.encryption_level);
   DCHECK(length);
 
   const size_t encrypted_length = framer_->EncryptInPlace(
@@ -691,6 +761,41 @@
   return packet_.packet_number_length;
 }
 
+QuicLongHeaderType QuicPacketCreator::GetLongHeaderType() const {
+  return (encryption_level_driven_long_header_type_
+              ? EncryptionlevelToLongHeaderType(packet_.encryption_level)
+              : long_header_type_);
+}
+
+QuicVariableLengthIntegerLength QuicPacketCreator::GetRetryTokenLengthLength()
+    const {
+  if (QuicVersionHasLongHeaderLengths(framer_->transport_version()) &&
+      HasIetfLongHeader() && GetLongHeaderType() == INITIAL) {
+    return QuicDataWriter::GetVarInt62Len(GetRetryToken().length());
+  }
+  return VARIABLE_LENGTH_INTEGER_LENGTH_0;
+}
+
+QuicStringPiece QuicPacketCreator::GetRetryToken() const {
+  return retry_token_;
+}
+
+void QuicPacketCreator::SetRetryToken(QuicStringPiece retry_token) {
+  retry_token_ = QuicString(retry_token);
+}
+
+QuicVariableLengthIntegerLength QuicPacketCreator::GetLengthLength() const {
+  if (QuicVersionHasLongHeaderLengths(framer_->transport_version()) &&
+      HasIetfLongHeader()) {
+    QuicLongHeaderType long_header_type = GetLongHeaderType();
+    if (long_header_type == INITIAL || long_header_type == ZERO_RTT_PROTECTED ||
+        long_header_type == HANDSHAKE) {
+      return VARIABLE_LENGTH_INTEGER_LENGTH_2;
+    }
+  }
+  return VARIABLE_LENGTH_INTEGER_LENGTH_0;
+}
+
 void QuicPacketCreator::FillPacketHeader(QuicPacketHeader* header) {
   header->destination_connection_id = connection_id_;
   header->destination_connection_id_length = GetDestinationConnectionIdLength();
@@ -711,10 +816,14 @@
   }
   header->packet_number = packet_.packet_number;
   header->packet_number_length = GetPacketNumberLength();
+  header->retry_token_length_length = GetRetryTokenLengthLength();
+  header->retry_token = GetRetryToken();
+  header->length_length = GetLengthLength();
+  header->remaining_packet_length = 0;
   if (!HasIetfLongHeader()) {
     return;
   }
-  header->long_packet_type = long_header_type_;
+  header->long_packet_type = GetLongHeaderType();
 }
 
 bool QuicPacketCreator::AddFrame(const QuicFrame& frame,
@@ -753,9 +862,7 @@
     }
     packet_.retransmittable_frames.push_back(frame);
     queued_frames_.push_back(frame);
-    if (frame.type == STREAM_FRAME &&
-        frame.stream_frame.stream_id ==
-            QuicUtils::GetCryptoStreamId(framer_->transport_version())) {
+    if (QuicUtils::IsHandshakeFrame(frame, framer_->transport_version())) {
       packet_.has_crypto_handshake = IS_HANDSHAKE;
     }
   } else {
@@ -819,7 +926,7 @@
 
 bool QuicPacketCreator::IncludeNonceInPublicHeader() const {
   return have_diversification_nonce_ &&
-         packet_.encryption_level == ENCRYPTION_INITIAL;
+         packet_.encryption_level == ENCRYPTION_ZERO_RTT;
 }
 
 bool QuicPacketCreator::IncludeVersionInHeader() const {
@@ -873,7 +980,8 @@
   const size_t packet_header_size = GetPacketHeaderSize(
       framer_->transport_version(), GetDestinationConnectionIdLength(),
       GetSourceConnectionIdLength(), IncludeVersionInHeader(),
-      IncludeNonceInPublicHeader(), GetPacketNumberLength());
+      IncludeNonceInPublicHeader(), GetPacketNumberLength(),
+      GetRetryTokenLengthLength(), GetRetryToken().length(), GetLengthLength());
   // This is the largest possible message payload when the length field is
   // omitted.
   return max_plaintext_size_ -
diff --git a/quic/core/quic_packet_creator.h b/quic/core/quic_packet_creator.h
index ef4fa37..c159026 100644
--- a/quic/core/quic_packet_creator.h
+++ b/quic/core/quic_packet_creator.h
@@ -88,6 +88,8 @@
       bool include_version,
       bool include_diversification_nonce,
       QuicPacketNumberLength packet_number_length,
+      QuicVariableLengthIntegerLength retry_token_length_length,
+      QuicVariableLengthIntegerLength length_length,
       QuicStreamOffset offset);
 
   // Returns false and flushes all pending frames if current open packet is
@@ -103,13 +105,21 @@
                    TransmissionType transmission_type,
                    QuicFrame* frame);
 
+  // Creates a CRYPTO frame that fits into the current packet (which must be
+  // empty) and adds it to the packet.
+  bool ConsumeCryptoData(EncryptionLevel level,
+                         size_t write_length,
+                         QuicStreamOffset offset,
+                         TransmissionType transmission_type,
+                         QuicFrame* frame);
+
   // Returns true if current open packet can accommodate more stream frames of
   // stream |id| at |offset| and data length |data_size|, false otherwise.
   bool HasRoomForStreamFrame(QuicStreamId id,
                              QuicStreamOffset offset,
                              size_t data_size);
 
-  // Returns true if current open packet can accomoodate a message frame of
+  // Returns true if current open packet can accommodate a message frame of
   // |length|.
   bool HasRoomForMessageFrame(QuicByteCount length);
 
@@ -242,6 +252,9 @@
   // Sets long header type of next constructed packets.
   void SetLongHeaderType(QuicLongHeaderType type);
 
+  // Sets the retry token to be sent over the wire in v99 IETF Initial packets.
+  void SetRetryToken(QuicStringPiece retry_token);
+
   // Returns the largest payload that will fit into a single MESSAGE frame.
   QuicPacketLength GetLargestMessagePayload() const;
 
@@ -276,6 +289,14 @@
                          bool fin,
                          QuicFrame* frame);
 
+  // Creates a CRYPTO frame which fits into the current open packet. Returns
+  // false if there isn't enough room in the current open packet for a CRYPTO
+  // frame, and true if there is.
+  bool CreateCryptoFrame(EncryptionLevel level,
+                         size_t write_length,
+                         QuicStreamOffset offset,
+                         QuicFrame* frame);
+
   void FillPacketHeader(QuicPacketHeader* header);
 
   // Adds a |frame| if there is space and returns false and flushes all pending
@@ -314,6 +335,21 @@
   // function instead.
   QuicPacketNumberLength GetPacketNumberLength() const;
 
+  // Returns long header type of packet to send over the wire.
+  QuicLongHeaderType GetLongHeaderType() const;
+
+  // Returns length of the retry token variable length integer to send over the
+  // wire. Is non-zero for v99 IETF Initial packets.
+  QuicVariableLengthIntegerLength GetRetryTokenLengthLength() const;
+
+  // Returns the retry token to send over the wire, only sent in
+  // v99 IETF Initial packets.
+  QuicStringPiece GetRetryToken() const;
+
+  // Returns length of the length variable length integer to send over the
+  // wire. Is non-zero for v99 IETF Initial, 0-RTT or Handshake packets.
+  QuicVariableLengthIntegerLength GetLengthLength() const;
+
   // Returns true if |frame| starts with CHLO.
   bool StreamFrameStartsWithChlo(const QuicStreamFrame& frame) const;
 
@@ -354,8 +390,13 @@
   SerializedPacket packet_;
 
   // Long header type of next constructed packets.
+  // TODO(fayang): remove this variable when deprecating
+  // quic_encryption_driven_header_type.
   QuicLongHeaderType long_header_type_;
 
+  // Retry token to send over the wire in v99 IETF Initial packets.
+  QuicString retry_token_;
+
   // Pending padding bytes to send. Pending padding bytes will be sent in next
   // packet(s) (after all other frames) if current constructed packet does not
   // have room to send all of them.
@@ -373,6 +414,9 @@
   // Latched value of --quic_set_transmission_type_for_next_frame. Don't use
   // this variable directly, use ShouldSetTransmissionTypeForNextFrame instead.
   bool set_transmission_type_for_next_frame_;
+
+  // Latched value of gfe2_reloadable_flag_quic_encryption_driven_header_type.
+  const bool encryption_level_driven_long_header_type_;
 };
 
 }  // namespace quic
diff --git a/quic/core/quic_packet_creator_test.cc b/quic/core/quic_packet_creator_test.cc
index 5dd4abb..cfc92ea 100644
--- a/quic/core/quic_packet_creator_test.cc
+++ b/quic/core/quic_packet_creator_test.cc
@@ -148,8 +148,8 @@
         creator_(connection_id_, &client_framer_, &delegate_, &producer_),
         serialized_packet_(creator_.NoPacket()) {
     EXPECT_CALL(delegate_, GetPacketBuffer()).WillRepeatedly(Return(nullptr));
-    creator_.SetEncrypter(ENCRYPTION_INITIAL, QuicMakeUnique<NullEncrypter>(
-                                                  Perspective::IS_CLIENT));
+    creator_.SetEncrypter(ENCRYPTION_ZERO_RTT, QuicMakeUnique<NullEncrypter>(
+                                                   Perspective::IS_CLIENT));
     creator_.SetEncrypter(
         ENCRYPTION_FORWARD_SECURE,
         QuicMakeUnique<NullEncrypter>(Perspective::IS_CLIENT));
@@ -203,7 +203,9 @@
         creator_.GetSourceConnectionIdLength(),
         QuicPacketCreatorPeer::SendVersionInPacket(&creator_),
         !kIncludeDiversificationNonce,
-        QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
+        QuicPacketCreatorPeer::GetPacketNumberLength(&creator_),
+        QuicPacketCreatorPeer::GetRetryTokenLengthLength(&creator_), 0,
+        QuicPacketCreatorPeer::GetLengthLength(&creator_));
   }
 
   // Returns the number of bytes of overhead that will be added to a packet
@@ -256,14 +258,15 @@
   TestPacketCreator creator_;
   SerializedPacket serialized_packet_;
   SimpleDataProducer producer_;
+  SimpleBufferAllocator allocator_;
 };
 
 // Run all packet creator tests with all supported versions of QUIC, and with
 // and without version in the packet header, as well as doing a run for each
 // length of truncated connection id.
-INSTANTIATE_TEST_CASE_P(QuicPacketCreatorTests,
-                        QuicPacketCreatorTest,
-                        ::testing::ValuesIn(GetTestParams()));
+INSTANTIATE_TEST_SUITE_P(QuicPacketCreatorTests,
+                         QuicPacketCreatorTest,
+                         ::testing::ValuesIn(GetTestParams()));
 
 TEST_P(QuicPacketCreatorTest, SerializeFrames) {
   for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; ++i) {
@@ -528,6 +531,15 @@
   ProcessPacket(serialized);
 }
 
+TEST_P(QuicPacketCreatorTest, ConsumeCryptoData) {
+  QuicString data = "crypto data";
+  QuicFrame frame;
+  ASSERT_TRUE(creator_.ConsumeCryptoData(ENCRYPTION_NONE, data.length(), 0,
+                                         NOT_RETRANSMISSION, &frame));
+  EXPECT_EQ(frame.crypto_frame->data_length, data.length());
+  EXPECT_TRUE(creator_.HasPendingFrames());
+}
+
 TEST_P(QuicPacketCreatorTest, ConsumeData) {
   QuicFrame frame;
   MakeIOVector("test", &iov_);
@@ -1113,7 +1125,8 @@
       creator_.GetDestinationConnectionIdLength(),
       creator_.GetSourceConnectionIdLength(),
       QuicPacketCreatorPeer::GetPacketNumberLength(&creator_),
-      &payload_length));
+      QuicPacketCreatorPeer::GetRetryTokenLengthLength(&creator_),
+      QuicPacketCreatorPeer::GetLengthLength(&creator_), &payload_length));
   QuicFrame frame;
   const QuicString too_long_payload(payload_length * 2, 'a');
   MakeIOVector(too_long_payload, &iov_);
@@ -1148,7 +1161,9 @@
                     creator_.GetSourceConnectionIdLength(),
                     QuicPacketCreatorPeer::SendVersionInPacket(&creator_),
                     !kIncludeDiversificationNonce,
-                    QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)),
+                    QuicPacketCreatorPeer::GetPacketNumberLength(&creator_),
+                    QuicPacketCreatorPeer::GetRetryTokenLengthLength(&creator_),
+                    0, QuicPacketCreatorPeer::GetLengthLength(&creator_)),
             creator_.BytesFree());
 
   // Add a variety of frame types and then a padding frame.
@@ -1202,7 +1217,9 @@
                     creator_.GetSourceConnectionIdLength(),
                     QuicPacketCreatorPeer::SendVersionInPacket(&creator_),
                     !kIncludeDiversificationNonce,
-                    QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)),
+                    QuicPacketCreatorPeer::GetPacketNumberLength(&creator_),
+                    QuicPacketCreatorPeer::GetRetryTokenLengthLength(&creator_),
+                    0, QuicPacketCreatorPeer::GetLengthLength(&creator_)),
             creator_.BytesFree());
 }
 
@@ -1489,22 +1506,29 @@
       .Times(3)
       .WillRepeatedly(
           Invoke(this, &QuicPacketCreatorTest::ClearSerializedPacketForTests));
+  QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
   // Verify that there is enough room for the largest message payload.
   EXPECT_TRUE(
       creator_.HasRoomForMessageFrame(creator_.GetLargestMessagePayload()));
   QuicString message(creator_.GetLargestMessagePayload(), 'a');
-  EXPECT_TRUE(creator_.AddSavedFrame(
-      QuicFrame(new QuicMessageFrame(1, message)), NOT_RETRANSMISSION));
+  QuicMessageFrame* message_frame = new QuicMessageFrame(1);
+  MakeSpan(&allocator_, message, &storage)
+      .SaveMemSlicesAsMessageData(message_frame);
+  EXPECT_TRUE(
+      creator_.AddSavedFrame(QuicFrame(message_frame), NOT_RETRANSMISSION));
   EXPECT_TRUE(creator_.HasPendingFrames());
   creator_.Flush();
 
-  EXPECT_TRUE(creator_.AddSavedFrame(
-      QuicFrame(new QuicMessageFrame(2, "message")), NOT_RETRANSMISSION));
+  QuicMessageFrame* frame2 = new QuicMessageFrame(2);
+  MakeSpan(&allocator_, "message", &storage).SaveMemSlicesAsMessageData(frame2);
+  EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(frame2), NOT_RETRANSMISSION));
   EXPECT_TRUE(creator_.HasPendingFrames());
   // Verify if a new frame is added, 1 byte message length will be added.
   EXPECT_EQ(1u, creator_.ExpansionOnNewFrame());
-  EXPECT_TRUE(creator_.AddSavedFrame(
-      QuicFrame(new QuicMessageFrame(3, "message2")), NOT_RETRANSMISSION));
+  QuicMessageFrame* frame3 = new QuicMessageFrame(3);
+  MakeSpan(&allocator_, "message2", &storage)
+      .SaveMemSlicesAsMessageData(frame3);
+  EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(frame3), NOT_RETRANSMISSION));
   EXPECT_EQ(1u, creator_.ExpansionOnNewFrame());
   creator_.Flush();
 
@@ -1513,16 +1537,17 @@
   EXPECT_TRUE(creator_.ConsumeData(
       QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_,
       1u, iov_.iov_len, 0u, 0u, false, false, NOT_RETRANSMISSION, &frame));
-  EXPECT_TRUE(creator_.AddSavedFrame(
-      QuicFrame(new QuicMessageFrame(1, "message")), NOT_RETRANSMISSION));
+  QuicMessageFrame* frame4 = new QuicMessageFrame(4);
+  MakeSpan(&allocator_, "message", &storage).SaveMemSlicesAsMessageData(frame4);
+  EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(frame4), NOT_RETRANSMISSION));
   EXPECT_TRUE(creator_.HasPendingFrames());
   // Verify there is not enough room for largest payload.
   EXPECT_FALSE(
       creator_.HasRoomForMessageFrame(creator_.GetLargestMessagePayload()));
   // Add largest message will causes the flush of the stream frame.
-  QuicMessageFrame message_frame(2, message);
-  EXPECT_FALSE(
-      creator_.AddSavedFrame(QuicFrame(&message_frame), NOT_RETRANSMISSION));
+  QuicMessageFrame frame5(5);
+  MakeSpan(&allocator_, message, &storage).SaveMemSlicesAsMessageData(&frame5);
+  EXPECT_FALSE(creator_.AddSavedFrame(QuicFrame(&frame5), NOT_RETRANSMISSION));
   EXPECT_FALSE(creator_.HasPendingFrames());
 }
 
@@ -1532,13 +1557,15 @@
   }
   QuicString message_data(kDefaultMaxPacketSize, 'a');
   QuicStringPiece message_buffer(message_data);
+  QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
   // Test all possible size of message frames.
   for (size_t message_size = 0;
        message_size <= creator_.GetLargestMessagePayload(); ++message_size) {
-    EXPECT_TRUE(creator_.AddSavedFrame(
-        QuicFrame(new QuicMessageFrame(
-            0, QuicStringPiece(message_buffer.data(), message_size))),
-        NOT_RETRANSMISSION));
+    QuicMessageFrame* frame = new QuicMessageFrame(0);
+    MakeSpan(&allocator_, QuicStringPiece(message_buffer.data(), message_size),
+             &storage)
+        .SaveMemSlicesAsMessageData(frame);
+    EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(frame), NOT_RETRANSMISSION));
     EXPECT_TRUE(creator_.HasPendingFrames());
 
     size_t expansion_bytes = message_size >= 64 ? 2 : 1;
@@ -1598,6 +1625,44 @@
   DeleteSerializedPacket();
 }
 
+TEST_P(QuicPacketCreatorTest, RetryToken) {
+  if (!GetParam().version_serialization ||
+      !QuicVersionHasLongHeaderLengths(client_framer_.transport_version())) {
+    return;
+  }
+
+  char retry_token_bytes[] = {1, 2,  3,  4,  5,  6,  7,  8,
+                              9, 10, 11, 12, 13, 14, 15, 16};
+
+  creator_.SetRetryToken(
+      QuicString(retry_token_bytes, sizeof(retry_token_bytes)));
+
+  frames_.push_back(QuicFrame(QuicStreamFrame(
+      QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), false,
+      0u, QuicStringPiece())));
+  SerializedPacket serialized = SerializeAllFrames(frames_);
+
+  QuicPacketHeader header;
+  {
+    InSequence s;
+    EXPECT_CALL(framer_visitor_, OnPacket());
+    EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+    EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+    EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+    EXPECT_CALL(framer_visitor_, OnPacketHeader(_))
+        .WillOnce(DoAll(SaveArg<0>(&header), Return(true)));
+    EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
+    EXPECT_CALL(framer_visitor_, OnPacketComplete());
+  }
+  ProcessPacket(serialized);
+  ASSERT_TRUE(header.version_flag);
+  ASSERT_EQ(header.long_packet_type, INITIAL);
+  ASSERT_EQ(header.retry_token.length(), sizeof(retry_token_bytes));
+  test::CompareCharArraysWithHexError(
+      "retry token", header.retry_token.data(), header.retry_token.length(),
+      retry_token_bytes, sizeof(retry_token_bytes));
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/quic_packet_generator.cc b/quic/core/quic_packet_generator.cc
index 12978a3..0b83030 100644
--- a/quic/core/quic_packet_generator.cc
+++ b/quic/core/quic_packet_generator.cc
@@ -56,6 +56,44 @@
   SendQueuedFrames(/*flush=*/false);
 }
 
+size_t QuicPacketGenerator::ConsumeCryptoData(EncryptionLevel level,
+                                              size_t write_length,
+                                              QuicStreamOffset offset) {
+  QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when "
+                                     "generator tries to write crypto data.";
+  // To make reasoning about crypto frames easier, we don't combine them with
+  // other retransmittable frames in a single packet.
+  // TODO(nharper): Once we have separate packet number spaces, everything
+  // should be driven by encryption level, and we should stop flushing in this
+  // spot.
+  const bool flush = packet_creator_.HasPendingRetransmittableFrames();
+  SendQueuedFrames(flush);
+
+  size_t total_bytes_consumed = 0;
+
+  while (total_bytes_consumed < write_length) {
+    QuicFrame frame;
+    if (!packet_creator_.ConsumeCryptoData(
+            level, write_length - total_bytes_consumed,
+            offset + total_bytes_consumed, next_transmission_type_, &frame)) {
+      // The only pending data in the packet is non-retransmittable frames. I'm
+      // assuming here that they won't occupy so much of the packet that a
+      // CRYPTO frame won't fit.
+      QUIC_BUG << "Failed to ConsumeCryptoData at level " << level;
+      return 0;
+    }
+    total_bytes_consumed += frame.crypto_frame->data_length;
+
+    // TODO(ianswett): Move to having the creator flush itself when it's full.
+    packet_creator_.Flush();
+  }
+
+  // Don't allow the handshake to be bundled with other retransmittable frames.
+  SendQueuedFrames(/*flush=*/true);
+
+  return total_bytes_consumed;
+}
+
 QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id,
                                                   size_t write_length,
                                                   QuicStreamOffset offset,
@@ -418,17 +456,19 @@
 }
 
 MessageStatus QuicPacketGenerator::AddMessageFrame(QuicMessageId message_id,
-                                                   QuicStringPiece message) {
+                                                   QuicMemSliceSpan message) {
   QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when "
                                      "generator tries to add message frame.";
-  if (message.length() > GetLargestMessagePayload()) {
+  const QuicByteCount message_length = message.total_length();
+  if (message_length > GetLargestMessagePayload()) {
     return MESSAGE_STATUS_TOO_LARGE;
   }
   SendQueuedFrames(/*flush=*/false);
-  if (!packet_creator_.HasRoomForMessageFrame(message.length())) {
+  if (!packet_creator_.HasRoomForMessageFrame(message_length)) {
     packet_creator_.Flush();
   }
-  QuicMessageFrame* frame = new QuicMessageFrame(message_id, message);
+  QuicMessageFrame* frame = new QuicMessageFrame(message_id);
+  message.SaveMemSlicesAsMessageData(frame);
   const bool success =
       packet_creator_.AddSavedFrame(QuicFrame(frame), next_transmission_type_);
   if (!success) {
diff --git a/quic/core/quic_packet_generator.h b/quic/core/quic_packet_generator.h
index 572d170..d50f38e 100644
--- a/quic/core/quic_packet_generator.h
+++ b/quic/core/quic_packet_generator.h
@@ -50,6 +50,7 @@
 #include "net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h"
 #include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h"
 
 namespace quic {
 
@@ -100,6 +101,14 @@
                                QuicStreamOffset offset,
                                StreamSendingState state);
 
+  // Consumes data for CRYPTO frames sent at |level| starting at |offset| for a
+  // total of |write_length| bytes, and returns the number of bytes consumed.
+  // The data is passed into the packet creator and serialized into one or more
+  // packets.
+  size_t ConsumeCryptoData(EncryptionLevel level,
+                           size_t write_length,
+                           QuicStreamOffset offset);
+
   // Sends as many data only packets as allowed by the send algorithm and the
   // available iov.
   // This path does not support padding, or bundling pending frames.
@@ -210,7 +219,7 @@
 
   // Tries to add a message frame containing |message| and returns the status.
   MessageStatus AddMessageFrame(QuicMessageId message_id,
-                                QuicStringPiece message);
+                                QuicMemSliceSpan message);
 
   // Returns the largest payload that will fit into a single MESSAGE frame.
   QuicPacketLength GetLargestMessagePayload() const;
diff --git a/quic/core/quic_packet_generator_test.cc b/quic/core/quic_packet_generator_test.cc
index b85a4f7..4a2dcc5 100644
--- a/quic/core/quic_packet_generator_test.cc
+++ b/quic/core/quic_packet_generator_test.cc
@@ -86,6 +86,7 @@
         num_rst_stream_frames(0),
         num_stop_waiting_frames(0),
         num_stream_frames(0),
+        num_crypto_frames(0),
         num_ping_frames(0),
         num_mtu_discovery_frames(0),
         num_padding_frames(0) {}
@@ -96,6 +97,7 @@
   size_t num_rst_stream_frames;
   size_t num_stop_waiting_frames;
   size_t num_stream_frames;
+  size_t num_crypto_frames;
   size_t num_ping_frames;
   size_t num_mtu_discovery_frames;
   size_t num_padding_frames;
@@ -140,6 +142,13 @@
     return QuicPacketGenerator::ConsumeData(id, total_length, offset, state);
   }
 
+  size_t ConsumeCryptoData(EncryptionLevel level,
+                           QuicStringPiece data,
+                           QuicStreamOffset offset) {
+    producer_->SaveCryptoData(level, offset, data);
+    return QuicPacketGenerator::ConsumeCryptoData(level, data.length(), offset);
+  }
+
   SimpleDataProducer* producer_;
 };
 
@@ -195,7 +204,7 @@
     size_t num_retransmittable_frames =
         contents.num_connection_close_frames + contents.num_goaway_frames +
         contents.num_rst_stream_frames + contents.num_stream_frames +
-        contents.num_ping_frames;
+        contents.num_crypto_frames + contents.num_ping_frames;
     size_t num_frames =
         contents.num_ack_frames + contents.num_stop_waiting_frames +
         contents.num_mtu_discovery_frames + contents.num_padding_frames +
@@ -222,6 +231,8 @@
               simple_framer_.rst_stream_frames().size());
     EXPECT_EQ(contents.num_stream_frames,
               simple_framer_.stream_frames().size());
+    EXPECT_EQ(contents.num_crypto_frames,
+              simple_framer_.crypto_frames().size());
     EXPECT_EQ(contents.num_stop_waiting_frames,
               simple_framer_.stop_waiting_frames().size());
     EXPECT_EQ(contents.num_padding_frames,
@@ -266,6 +277,7 @@
   std::vector<SerializedPacket> packets_;
   QuicAckFrame ack_frame_;
   struct iovec iov_;
+  SimpleBufferAllocator allocator_;
 
  private:
   std::unique_ptr<char[]> data_array_;
@@ -399,6 +411,23 @@
   CheckPacketContains(contents, 0);
 }
 
+TEST_F(QuicPacketGeneratorTest, ConsumeCryptoData) {
+  EXPECT_CALL(delegate_, OnSerializedPacket(_))
+      .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+  QuicString data = "crypto data";
+  size_t consumed_bytes =
+      generator_.ConsumeCryptoData(ENCRYPTION_NONE, data, 0);
+  generator_.Flush();
+  EXPECT_EQ(data.length(), consumed_bytes);
+  EXPECT_FALSE(generator_.HasQueuedFrames());
+  EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+  PacketContents contents;
+  contents.num_crypto_frames = 1;
+  contents.num_padding_frames = 1;
+  CheckPacketContains(contents, 0);
+}
+
 TEST_F(QuicPacketGeneratorTest, ConsumeData_NotWritable) {
   delegate_.SetCanNotWrite();
 
@@ -555,22 +584,25 @@
 TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) {
   // Set the packet size be enough for two stream frames with 0 stream offset,
   // but not enough for a stream frame of 0 offset and one with non-zero offset.
-  size_t length = NullEncrypter(Perspective::IS_CLIENT).GetCiphertextSize(0) +
-                  GetPacketHeaderSize(
-                      framer_.transport_version(),
-                      creator_->GetDestinationConnectionIdLength(),
-                      creator_->GetSourceConnectionIdLength(),
-                      QuicPacketCreatorPeer::SendVersionInPacket(creator_),
-                      !kIncludeDiversificationNonce,
-                      QuicPacketCreatorPeer::GetPacketNumberLength(creator_)) +
-                  // Add an extra 3 bytes for the payload and 1 byte so
-                  // BytesFree is larger than the GetMinStreamFrameSize.
-                  QuicFramer::GetMinStreamFrameSize(framer_.transport_version(),
-                                                    1, 0, false, 3) +
-                  3 +
-                  QuicFramer::GetMinStreamFrameSize(framer_.transport_version(),
-                                                    1, 0, true, 1) +
-                  1;
+  size_t length =
+      NullEncrypter(Perspective::IS_CLIENT).GetCiphertextSize(0) +
+      GetPacketHeaderSize(
+          framer_.transport_version(),
+          creator_->GetDestinationConnectionIdLength(),
+          creator_->GetSourceConnectionIdLength(),
+          QuicPacketCreatorPeer::SendVersionInPacket(creator_),
+          !kIncludeDiversificationNonce,
+          QuicPacketCreatorPeer::GetPacketNumberLength(creator_),
+          QuicPacketCreatorPeer::GetRetryTokenLengthLength(creator_), 0,
+          QuicPacketCreatorPeer::GetLengthLength(creator_)) +
+      // Add an extra 3 bytes for the payload and 1 byte so
+      // BytesFree is larger than the GetMinStreamFrameSize.
+      QuicFramer::GetMinStreamFrameSize(framer_.transport_version(), 1, 0,
+                                        false, 3) +
+      3 +
+      QuicFramer::GetMinStreamFrameSize(framer_.transport_version(), 1, 0, true,
+                                        1) +
+      1;
   generator_.SetMaxPacketLength(length);
   delegate_.SetCanWriteAnything();
   {
@@ -1227,19 +1259,22 @@
   const QuicStreamId kDataStreamId = 5;
   // Set the packet size be enough for one stream frame with 0 stream offset and
   // max size of random padding.
-  size_t length = NullEncrypter(Perspective::IS_CLIENT).GetCiphertextSize(0) +
-                  GetPacketHeaderSize(
-                      framer_.transport_version(),
-                      creator_->GetDestinationConnectionIdLength(),
-                      creator_->GetSourceConnectionIdLength(),
-                      QuicPacketCreatorPeer::SendVersionInPacket(creator_),
-                      !kIncludeDiversificationNonce,
-                      QuicPacketCreatorPeer::GetPacketNumberLength(creator_)) +
-                  QuicFramer::GetMinStreamFrameSize(
-                      framer_.transport_version(), kDataStreamId, 0,
-                      /*last_frame_in_packet=*/false,
-                      kStreamFramePayloadSize + kMaxNumRandomPaddingBytes) +
-                  kStreamFramePayloadSize + kMaxNumRandomPaddingBytes;
+  size_t length =
+      NullEncrypter(Perspective::IS_CLIENT).GetCiphertextSize(0) +
+      GetPacketHeaderSize(
+          framer_.transport_version(),
+          creator_->GetDestinationConnectionIdLength(),
+          creator_->GetSourceConnectionIdLength(),
+          QuicPacketCreatorPeer::SendVersionInPacket(creator_),
+          !kIncludeDiversificationNonce,
+          QuicPacketCreatorPeer::GetPacketNumberLength(creator_),
+          QuicPacketCreatorPeer::GetRetryTokenLengthLength(creator_), 0,
+          QuicPacketCreatorPeer::GetLengthLength(creator_)) +
+      QuicFramer::GetMinStreamFrameSize(
+          framer_.transport_version(), kDataStreamId, 0,
+          /*last_frame_in_packet=*/false,
+          kStreamFramePayloadSize + kMaxNumRandomPaddingBytes) +
+      kStreamFramePayloadSize + kMaxNumRandomPaddingBytes;
   generator_.SetMaxPacketLength(length);
   delegate_.SetCanWriteAnything();
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
@@ -1275,7 +1310,9 @@
           creator_->GetSourceConnectionIdLength(),
           QuicPacketCreatorPeer::SendVersionInPacket(creator_),
           !kIncludeDiversificationNonce,
-          QuicPacketCreatorPeer::GetPacketNumberLength(creator_)) +
+          QuicPacketCreatorPeer::GetPacketNumberLength(creator_),
+          QuicPacketCreatorPeer::GetRetryTokenLengthLength(creator_), 0,
+          QuicPacketCreatorPeer::GetLengthLength(creator_)) +
       QuicFramer::GetMinStreamFrameSize(
           framer_.transport_version(), kDataStreamId, 0,
           /*last_frame_in_packet=*/false, kStreamFramePayloadSize + 1) +
@@ -1315,22 +1352,25 @@
   const QuicStreamId kDataStreamId2 = 6;
   // Set the packet size be enough for first frame with 0 stream offset + second
   // frame + 1 byte payload. two or more packets will accommodate.
-  size_t length = NullEncrypter(Perspective::IS_CLIENT).GetCiphertextSize(0) +
-                  GetPacketHeaderSize(
-                      framer_.transport_version(),
-                      creator_->GetDestinationConnectionIdLength(),
-                      creator_->GetSourceConnectionIdLength(),
-                      QuicPacketCreatorPeer::SendVersionInPacket(creator_),
-                      !kIncludeDiversificationNonce,
-                      QuicPacketCreatorPeer::GetPacketNumberLength(creator_)) +
-                  QuicFramer::GetMinStreamFrameSize(
-                      framer_.transport_version(), kDataStreamId1, 0,
-                      /*last_frame_in_packet=*/false, kStreamFramePayloadSize) +
-                  kStreamFramePayloadSize +
-                  QuicFramer::GetMinStreamFrameSize(
-                      framer_.transport_version(), kDataStreamId1, 0,
-                      /*last_frame_in_packet=*/false, 1) +
-                  1;
+  size_t length =
+      NullEncrypter(Perspective::IS_CLIENT).GetCiphertextSize(0) +
+      GetPacketHeaderSize(
+          framer_.transport_version(),
+          creator_->GetDestinationConnectionIdLength(),
+          creator_->GetSourceConnectionIdLength(),
+          QuicPacketCreatorPeer::SendVersionInPacket(creator_),
+          !kIncludeDiversificationNonce,
+          QuicPacketCreatorPeer::GetPacketNumberLength(creator_),
+          QuicPacketCreatorPeer::GetRetryTokenLengthLength(creator_), 0,
+          QuicPacketCreatorPeer::GetLengthLength(creator_)) +
+      QuicFramer::GetMinStreamFrameSize(
+          framer_.transport_version(), kDataStreamId1, 0,
+          /*last_frame_in_packet=*/false, kStreamFramePayloadSize) +
+      kStreamFramePayloadSize +
+      QuicFramer::GetMinStreamFrameSize(framer_.transport_version(),
+                                        kDataStreamId1, 0,
+                                        /*last_frame_in_packet=*/false, 1) +
+      1;
   generator_.SetMaxPacketLength(length);
   delegate_.SetCanWriteAnything();
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
@@ -1370,6 +1410,7 @@
   if (framer_.transport_version() <= QUIC_VERSION_44) {
     return;
   }
+  quic::QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
   delegate_.SetCanWriteAnything();
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
       .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
@@ -1378,21 +1419,29 @@
   generator_.ConsumeData(
       QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u,
       iov_.iov_len, 0, FIN);
-  EXPECT_EQ(MESSAGE_STATUS_SUCCESS, generator_.AddMessageFrame(1, "message"));
+  EXPECT_EQ(MESSAGE_STATUS_SUCCESS,
+            generator_.AddMessageFrame(
+                1, MakeSpan(&allocator_, "message", &storage)));
   EXPECT_TRUE(generator_.HasQueuedFrames());
   EXPECT_TRUE(generator_.HasRetransmittableFrames());
 
   // Add a message which causes the flush of current packet.
-  EXPECT_EQ(MESSAGE_STATUS_SUCCESS,
-            generator_.AddMessageFrame(
-                2, QuicString(generator_.GetLargestMessagePayload(), 'a')));
+  EXPECT_EQ(
+      MESSAGE_STATUS_SUCCESS,
+      generator_.AddMessageFrame(
+          2, MakeSpan(&allocator_,
+                      QuicString(generator_.GetLargestMessagePayload(), 'a'),
+                      &storage)));
   EXPECT_TRUE(generator_.HasRetransmittableFrames());
 
   // Failed to send messages which cannot fit into one packet.
   EXPECT_EQ(
       MESSAGE_STATUS_TOO_LARGE,
       generator_.AddMessageFrame(
-          3, QuicString(generator_.GetLargestMessagePayload() + 10, 'a')));
+          3,
+          MakeSpan(&allocator_,
+                   QuicString(generator_.GetLargestMessagePayload() + 10, 'a'),
+                   &storage)));
 }
 
 }  // namespace test
diff --git a/quic/core/quic_packet_number.cc b/quic/core/quic_packet_number.cc
index f0a497f..8a799c1 100644
--- a/quic/core/quic_packet_number.cc
+++ b/quic/core/quic_packet_number.cc
@@ -64,9 +64,9 @@
 #ifndef NDEBUG
   DCHECK(IsInitialized());
   if (GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) {
-    DCHECK_GE(ToUint64(), 1);
+    DCHECK_GE(ToUint64(), 1UL);
   } else {
-    DCHECK_GT(ToUint64(), 1);
+    DCHECK_GT(ToUint64(), 1UL);
   }
 #endif
   packet_number_--;
@@ -77,9 +77,9 @@
 #ifndef NDEBUG
   DCHECK(IsInitialized());
   if (GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) {
-    DCHECK_GE(ToUint64(), 1);
+    DCHECK_GE(ToUint64(), 1UL);
   } else {
-    DCHECK_GT(ToUint64(), 1);
+    DCHECK_GT(ToUint64(), 1UL);
   }
 #endif
   QuicPacketNumber previous(*this);
diff --git a/quic/core/quic_packet_reader.cc b/quic/core/quic_packet_reader.cc
index fc2ac15..2763b34 100644
--- a/quic/core/quic_packet_reader.cc
+++ b/quic/core/quic_packet_reader.cc
@@ -20,6 +20,7 @@
 #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_server_stats.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
 #include "net/quic/platform/impl/quic_socket_utils.h"
 
@@ -115,9 +116,12 @@
     }
 
     if (QUIC_PREDICT_FALSE(mmsg_hdr_[i].msg_hdr.msg_flags & MSG_TRUNC)) {
-      QUIC_LOG_FIRST_N(ERROR, 10)
+      QUIC_LOG_FIRST_N(WARNING, 100)
           << "Dropping truncated QUIC packet: buffer size:"
           << packets_[i].iov.iov_len << " packet size:" << mmsg_hdr_[i].msg_len;
+      QUIC_SERVER_HISTOGRAM_COUNTS(
+          "QuicPacketReader.DroppedPacketSize", mmsg_hdr_[i].msg_len, 1, 10000,
+          20, "In QuicPacketReader, the size of big packets that are dropped.");
       continue;
     }
 
diff --git a/quic/core/quic_packet_writer.h b/quic/core/quic_packet_writer.h
index 3314701..c33e9b7 100644
--- a/quic/core/quic_packet_writer.h
+++ b/quic/core/quic_packet_writer.h
@@ -84,11 +84,6 @@
                                   const QuicSocketAddress& peer_address,
                                   PerPacketOptions* options) = 0;
 
-  // Returns true if the writer buffers and subsequently rewrites data
-  // when an attempt to write results in the underlying socket becoming
-  // write blocked.
-  virtual bool IsWriteBlockedDataBuffered() const = 0;
-
   // Returns true if the network socket is not writable.
   virtual bool IsWriteBlocked() const = 0;
 
diff --git a/quic/core/quic_packet_writer_wrapper.cc b/quic/core/quic_packet_writer_wrapper.cc
index 248bf82..f1f25d8 100644
--- a/quic/core/quic_packet_writer_wrapper.cc
+++ b/quic/core/quic_packet_writer_wrapper.cc
@@ -24,10 +24,6 @@
                               options);
 }
 
-bool QuicPacketWriterWrapper::IsWriteBlockedDataBuffered() const {
-  return writer_->IsWriteBlockedDataBuffered();
-}
-
 bool QuicPacketWriterWrapper::IsWriteBlocked() const {
   return writer_->IsWriteBlocked();
 }
diff --git a/quic/core/quic_packet_writer_wrapper.h b/quic/core/quic_packet_writer_wrapper.h
index b731064..64b10f5 100644
--- a/quic/core/quic_packet_writer_wrapper.h
+++ b/quic/core/quic_packet_writer_wrapper.h
@@ -30,7 +30,6 @@
                           const QuicIpAddress& self_address,
                           const QuicSocketAddress& peer_address,
                           PerPacketOptions* options) override;
-  bool IsWriteBlockedDataBuffered() const override;
   bool IsWriteBlocked() const override;
   void SetWritable() override;
   QuicByteCount GetMaxPacketSize(
diff --git a/quic/core/quic_packets.cc b/quic/core/quic_packets.cc
index 5e3ba76..31cdb7d 100644
--- a/quic/core/quic_packets.cc
+++ b/quic/core/quic_packets.cc
@@ -4,11 +4,13 @@
 
 #include "net/third_party/quiche/src/quic/core/quic_packets.h"
 
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
 #include "net/third_party/quiche/src/quic/core/quic_versions.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
 
 namespace quic {
@@ -18,7 +20,9 @@
   return GetPacketHeaderSize(version, header.destination_connection_id_length,
                              header.source_connection_id_length,
                              header.version_flag, header.nonce != nullptr,
-                             header.packet_number_length);
+                             header.packet_number_length,
+                             header.retry_token_length_length,
+                             header.retry_token.length(), header.length_length);
 }
 
 size_t GetPacketHeaderSize(
@@ -27,16 +31,20 @@
     QuicConnectionIdLength source_connection_id_length,
     bool include_version,
     bool include_diversification_nonce,
-    QuicPacketNumberLength packet_number_length) {
+    QuicPacketNumberLength packet_number_length,
+    QuicVariableLengthIntegerLength retry_token_length_length,
+    QuicByteCount retry_token_length,
+    QuicVariableLengthIntegerLength length_length) {
   if (version > QUIC_VERSION_43) {
     if (include_version) {
       // Long header.
       return kPacketHeaderTypeSize + kConnectionIdLengthSize +
              destination_connection_id_length + source_connection_id_length +
-             (version > QUIC_VERSION_46 ? packet_number_length
+             (version > QUIC_VERSION_44 ? packet_number_length
                                         : PACKET_4BYTE_PACKET_NUMBER) +
              kQuicVersionSize +
-             (include_diversification_nonce ? kDiversificationNonceSize : 0);
+             (include_diversification_nonce ? kDiversificationNonceSize : 0) +
+             retry_token_length_length + retry_token_length + length_length;
     }
     // Short header.
     return kPacketHeaderTypeSize + destination_connection_id_length +
@@ -58,11 +66,15 @@
     QuicConnectionIdLength source_connection_id_length,
     bool include_version,
     bool include_diversification_nonce,
-    QuicPacketNumberLength packet_number_length) {
+    QuicPacketNumberLength packet_number_length,
+    QuicVariableLengthIntegerLength retry_token_length_length,
+    QuicByteCount retry_token_length,
+    QuicVariableLengthIntegerLength length_length) {
   // Encryption starts before private flags.
   return GetPacketHeaderSize(
       version, destination_connection_id_length, source_connection_id_length,
-      include_version, include_diversification_nonce, packet_number_length);
+      include_version, include_diversification_nonce, packet_number_length,
+      retry_token_length_length, retry_token_length, length_length);
 }
 
 QuicPacketHeader::QuicPacketHeader()
@@ -78,7 +90,11 @@
       nonce(nullptr),
       form(GOOGLE_QUIC_PACKET),
       long_packet_type(INITIAL),
-      possible_stateless_reset_token(0) {}
+      possible_stateless_reset_token(0),
+      retry_token_length_length(VARIABLE_LENGTH_INTEGER_LENGTH_0),
+      retry_token(QuicStringPiece()),
+      length_length(VARIABLE_LENGTH_INTEGER_LENGTH_0),
+      remaining_packet_length(0) {}
 
 QuicPacketHeader::QuicPacketHeader(const QuicPacketHeader& other) = default;
 
@@ -126,6 +142,23 @@
      << ", version_flag: " << header.version_flag;
   if (header.version_flag) {
     os << ", version: " << ParsedQuicVersionToString(header.version);
+    if (header.long_packet_type != INVALID_PACKET_TYPE) {
+      os << ", long_packet_type: "
+         << QuicUtils::QuicLongHeaderTypetoString(header.long_packet_type);
+    }
+    if (header.retry_token_length_length != VARIABLE_LENGTH_INTEGER_LENGTH_0) {
+      os << ", retry_token_length_length: "
+         << static_cast<int>(header.retry_token_length_length);
+    }
+    if (header.retry_token.length() != 0) {
+      os << ", retry_token_length: " << header.retry_token.length();
+    }
+    if (header.length_length != VARIABLE_LENGTH_INTEGER_LENGTH_0) {
+      os << ", length_length: " << static_cast<int>(header.length_length);
+    }
+    if (header.remaining_packet_length != 0) {
+      os << ", remaining_packet_length: " << header.remaining_packet_length;
+    }
   }
   if (header.nonce != nullptr) {
     os << ", diversification_nonce: "
@@ -148,21 +181,44 @@
   }
 }
 
-QuicPacket::QuicPacket(char* buffer,
-                       size_t length,
-                       bool owns_buffer,
-                       QuicConnectionIdLength destination_connection_id_length,
-                       QuicConnectionIdLength source_connection_id_length,
-                       bool includes_version,
-                       bool includes_diversification_nonce,
-                       QuicPacketNumberLength packet_number_length)
+QuicPacket::QuicPacket(
+    char* buffer,
+    size_t length,
+    bool owns_buffer,
+    QuicConnectionIdLength destination_connection_id_length,
+    QuicConnectionIdLength source_connection_id_length,
+    bool includes_version,
+    bool includes_diversification_nonce,
+    QuicPacketNumberLength packet_number_length,
+    QuicVariableLengthIntegerLength retry_token_length_length,
+    QuicByteCount retry_token_length,
+    QuicVariableLengthIntegerLength length_length)
     : QuicData(buffer, length, owns_buffer),
       buffer_(buffer),
       destination_connection_id_length_(destination_connection_id_length),
       source_connection_id_length_(source_connection_id_length),
       includes_version_(includes_version),
       includes_diversification_nonce_(includes_diversification_nonce),
-      packet_number_length_(packet_number_length) {}
+      packet_number_length_(packet_number_length),
+      retry_token_length_length_(retry_token_length_length),
+      retry_token_length_(retry_token_length),
+      length_length_(length_length) {}
+
+QuicPacket::QuicPacket(char* buffer,
+                       size_t length,
+                       bool owns_buffer,
+                       const QuicPacketHeader& header)
+    : QuicPacket(buffer,
+                 length,
+                 owns_buffer,
+                 header.destination_connection_id_length,
+                 header.source_connection_id_length,
+                 header.version_flag,
+                 header.nonce != nullptr,
+                 header.packet_number_length,
+                 header.retry_token_length_length,
+                 header.retry_token.length(),
+                 header.length_length) {}
 
 QuicEncryptedPacket::QuicEncryptedPacket(const char* buffer, size_t length)
     : QuicData(buffer, length) {}
@@ -262,17 +318,19 @@
 
 QuicStringPiece QuicPacket::AssociatedData(QuicTransportVersion version) const {
   return QuicStringPiece(
-      data(), GetStartOfEncryptedData(
-                  version, destination_connection_id_length_,
-                  source_connection_id_length_, includes_version_,
-                  includes_diversification_nonce_, packet_number_length_));
+      data(),
+      GetStartOfEncryptedData(version, destination_connection_id_length_,
+                              source_connection_id_length_, includes_version_,
+                              includes_diversification_nonce_,
+                              packet_number_length_, retry_token_length_length_,
+                              retry_token_length_, length_length_));
 }
 
 QuicStringPiece QuicPacket::Plaintext(QuicTransportVersion version) const {
   const size_t start_of_encrypted_data = GetStartOfEncryptedData(
       version, destination_connection_id_length_, source_connection_id_length_,
-      includes_version_, includes_diversification_nonce_,
-      packet_number_length_);
+      includes_version_, includes_diversification_nonce_, packet_number_length_,
+      retry_token_length_length_, retry_token_length_, length_length_);
   return QuicStringPiece(data() + start_of_encrypted_data,
                          length() - start_of_encrypted_data);
 }
diff --git a/quic/core/quic_packets.h b/quic/core/quic_packets.h
index a89ddbb..8808ad2 100644
--- a/quic/core/quic_packets.h
+++ b/quic/core/quic_packets.h
@@ -5,6 +5,7 @@
 #ifndef QUICHE_QUIC_CORE_QUIC_PACKETS_H_
 #define QUICHE_QUIC_CORE_QUIC_PACKETS_H_
 
+#include <cstddef>
 #include <cstdint>
 #include <limits>
 #include <list>
@@ -42,20 +43,26 @@
                     QuicConnectionIdLength source_connection_id_length,
                     bool include_version,
                     bool include_diversification_nonce,
-                    QuicPacketNumberLength packet_number_length);
+                    QuicPacketNumberLength packet_number_length,
+                    QuicVariableLengthIntegerLength retry_token_length_length,
+                    QuicByteCount retry_token_length,
+                    QuicVariableLengthIntegerLength length_length);
 
 // Index of the first byte in a QUIC packet of encrypted data.
 QUIC_EXPORT_PRIVATE size_t
 GetStartOfEncryptedData(QuicTransportVersion version,
                         const QuicPacketHeader& header);
 
-QUIC_EXPORT_PRIVATE size_t
-GetStartOfEncryptedData(QuicTransportVersion version,
-                        QuicConnectionIdLength destination_connection_id_length,
-                        QuicConnectionIdLength source_connection_id_length,
-                        bool include_version,
-                        bool include_diversification_nonce,
-                        QuicPacketNumberLength packet_number_length);
+QUIC_EXPORT_PRIVATE size_t GetStartOfEncryptedData(
+    QuicTransportVersion version,
+    QuicConnectionIdLength destination_connection_id_length,
+    QuicConnectionIdLength source_connection_id_length,
+    bool include_version,
+    bool include_diversification_nonce,
+    QuicPacketNumberLength packet_number_length,
+    QuicVariableLengthIntegerLength retry_token_length_length,
+    QuicByteCount retry_token_length,
+    QuicVariableLengthIntegerLength length_length);
 
 struct QUIC_EXPORT_PRIVATE QuicPacketHeader {
   QuicPacketHeader();
@@ -94,6 +101,18 @@
   // Stores last 16 bytes of a this packet, used to check whether this packet is
   // a stateless reset packet on decryption failure.
   QuicUint128 possible_stateless_reset_token;
+  // Length of the retry token length variable length integer field,
+  // carried only by v99 IETF Initial packets.
+  QuicVariableLengthIntegerLength retry_token_length_length;
+  // Retry token, carried only by v99 IETF Initial packets.
+  QuicStringPiece retry_token;
+  // Length of the length variable length integer field,
+  // carried only by v99 IETF Initial, 0-RTT and Handshake packets.
+  QuicVariableLengthIntegerLength length_length;
+  // Length of the packet number and payload, carried only by v99 IETF Initial,
+  // 0-RTT and Handshake packets. Also includes the length of the
+  // diversification nonce in server to client 0-RTT packets.
+  QuicByteCount remaining_packet_length;
 };
 
 struct QUIC_EXPORT_PRIVATE QuicPublicResetPacket {
@@ -153,9 +172,6 @@
 
 class QUIC_EXPORT_PRIVATE QuicPacket : public QuicData {
  public:
-  // TODO(fayang): 3 fields from header are passed in as arguments.
-  // Consider to add a convenience method which directly accepts the entire
-  // header.
   QuicPacket(char* buffer,
              size_t length,
              bool owns_buffer,
@@ -163,7 +179,14 @@
              QuicConnectionIdLength source_connection_id_length,
              bool includes_version,
              bool includes_diversification_nonce,
-             QuicPacketNumberLength packet_number_length);
+             QuicPacketNumberLength packet_number_length,
+             QuicVariableLengthIntegerLength retry_token_length_length,
+             QuicByteCount retry_token_length,
+             QuicVariableLengthIntegerLength length_length);
+  QuicPacket(char* buffer,
+             size_t length,
+             bool owns_buffer,
+             const QuicPacketHeader& header);
   QuicPacket(const QuicPacket&) = delete;
   QuicPacket& operator=(const QuicPacket&) = delete;
 
@@ -179,6 +202,9 @@
   const bool includes_version_;
   const bool includes_diversification_nonce_;
   const QuicPacketNumberLength packet_number_length_;
+  const QuicVariableLengthIntegerLength retry_token_length_length_;
+  const QuicByteCount retry_token_length_;
+  const QuicVariableLengthIntegerLength length_length_;
 };
 
 class QUIC_EXPORT_PRIVATE QuicEncryptedPacket : public QuicData {
diff --git a/quic/core/quic_received_packet_manager.cc b/quic/core/quic_received_packet_manager.cc
index 8189ff8..a9e422f 100644
--- a/quic/core/quic_received_packet_manager.cc
+++ b/quic/core/quic_received_packet_manager.cc
@@ -4,6 +4,7 @@
 
 #include "net/third_party/quiche/src/quic/core/quic_received_packet_manager.h"
 
+#include <algorithm>
 #include <limits>
 #include <utility>
 
@@ -75,6 +76,13 @@
           std::make_pair(packet_number, receipt_time));
     }
   }
+
+  if (least_received_packet_number_.IsInitialized()) {
+    least_received_packet_number_ =
+        std::min(least_received_packet_number_, packet_number);
+  } else {
+    least_received_packet_number_ = packet_number;
+  }
 }
 
 bool QuicReceivedPacketManager::IsMissing(QuicPacketNumber packet_number) {
@@ -150,12 +158,14 @@
   if (ack_frame_.packets.NumIntervals() > 1) {
     return true;
   }
-  // TODO(fayang): Fix this as this check assumes first sent packet by peer
-  // is 1.
-  return ack_frame_.packets.Min() >
-         (peer_least_packet_awaiting_ack_.IsInitialized()
-              ? peer_least_packet_awaiting_ack_
-              : QuicPacketNumber(1));
+  if (!GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    return ack_frame_.packets.Min() >
+           (peer_least_packet_awaiting_ack_.IsInitialized()
+                ? peer_least_packet_awaiting_ack_
+                : QuicPacketNumber(1));
+  }
+  return peer_least_packet_awaiting_ack_.IsInitialized() &&
+         ack_frame_.packets.Min() > peer_least_packet_awaiting_ack_;
 }
 
 bool QuicReceivedPacketManager::HasNewMissingPackets() const {
@@ -171,4 +181,16 @@
   return LargestAcked(ack_frame_);
 }
 
+QuicPacketNumber QuicReceivedPacketManager::PeerFirstSendingPacketNumber()
+    const {
+  if (!GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    return QuicPacketNumber(1);
+  }
+  if (!least_received_packet_number_.IsInitialized()) {
+    QUIC_BUG << "No packets have been received yet";
+    return QuicPacketNumber(1);
+  }
+  return least_received_packet_number_;
+}
+
 }  // namespace quic
diff --git a/quic/core/quic_received_packet_manager.h b/quic/core/quic_received_packet_manager.h
index c720a01..12d1f9c 100644
--- a/quic/core/quic_received_packet_manager.h
+++ b/quic/core/quic_received_packet_manager.h
@@ -65,6 +65,13 @@
 
   QuicPacketNumber GetLargestObserved() const;
 
+  // Returns peer first sending packet number to our best knowledge. If
+  // GetQuicRestartFlag(quic_enable_accept_random_ipn) is false, returns 1.
+  // Otherwise considers least_received_packet_number_ as peer first sending
+  // packet number. Please note, this function should only be called when at
+  // least one packet has been received.
+  QuicPacketNumber PeerFirstSendingPacketNumber() const;
+
   // For logging purposes.
   const QuicAckFrame& ack_frame() const { return ack_frame_; }
 
@@ -101,6 +108,9 @@
   // If true, save timestamps in the ack_frame_.
   bool save_timestamps_;
 
+  // Least packet number received from peer.
+  QuicPacketNumber least_received_packet_number_;
+
   QuicConnectionStats* stats_;
 };
 
diff --git a/quic/core/quic_received_packet_manager_test.cc b/quic/core/quic_received_packet_manager_test.cc
index b8264a5..9cbc6e7 100644
--- a/quic/core/quic_received_packet_manager_test.cc
+++ b/quic/core/quic_received_packet_manager_test.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "net/third_party/quiche/src/quic/core/quic_connection_stats.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
 
 namespace quic {
@@ -56,9 +57,9 @@
   QuicReceivedPacketManager received_manager_;
 };
 
-INSTANTIATE_TEST_CASE_P(QuicReceivedPacketManagerTest,
-                        QuicReceivedPacketManagerTest,
-                        ::testing::ValuesIn(GetTestParams()));
+INSTANTIATE_TEST_SUITE_P(QuicReceivedPacketManagerTest,
+                         QuicReceivedPacketManagerTest,
+                         ::testing::ValuesIn(GetTestParams()));
 
 TEST_P(QuicReceivedPacketManagerTest, DontWaitForPacketsBefore) {
   QuicPacketHeader header;
@@ -159,6 +160,44 @@
   EXPECT_EQ(2u, received_manager_.ack_frame().received_packet_times.size());
 }
 
+TEST_P(QuicReceivedPacketManagerTest, HasMissingPackets) {
+  if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    EXPECT_QUIC_BUG(received_manager_.PeerFirstSendingPacketNumber(),
+                    "No packets have been received yet");
+  } else {
+    EXPECT_EQ(QuicPacketNumber(1),
+              received_manager_.PeerFirstSendingPacketNumber());
+  }
+  RecordPacketReceipt(4, QuicTime::Zero());
+  if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    EXPECT_EQ(QuicPacketNumber(4),
+              received_manager_.PeerFirstSendingPacketNumber());
+    EXPECT_FALSE(received_manager_.HasMissingPackets());
+  } else {
+    EXPECT_TRUE(received_manager_.HasMissingPackets());
+    EXPECT_EQ(QuicPacketNumber(1),
+              received_manager_.PeerFirstSendingPacketNumber());
+  }
+  RecordPacketReceipt(3, QuicTime::Zero());
+  if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    EXPECT_FALSE(received_manager_.HasMissingPackets());
+    EXPECT_EQ(QuicPacketNumber(3),
+              received_manager_.PeerFirstSendingPacketNumber());
+  } else {
+    EXPECT_TRUE(received_manager_.HasMissingPackets());
+    EXPECT_EQ(QuicPacketNumber(1),
+              received_manager_.PeerFirstSendingPacketNumber());
+  }
+  RecordPacketReceipt(1, QuicTime::Zero());
+  EXPECT_EQ(QuicPacketNumber(1),
+            received_manager_.PeerFirstSendingPacketNumber());
+  EXPECT_TRUE(received_manager_.HasMissingPackets());
+  RecordPacketReceipt(2, QuicTime::Zero());
+  EXPECT_EQ(QuicPacketNumber(1),
+            received_manager_.PeerFirstSendingPacketNumber());
+  EXPECT_FALSE(received_manager_.HasMissingPackets());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc
index 4bbe070..49c5e08 100644
--- a/quic/core/quic_sent_packet_manager.cc
+++ b/quic/core/quic_sent_packet_manager.cc
@@ -67,8 +67,9 @@
 
 }  // namespace
 
-#define ENDPOINT \
-  (perspective_ == Perspective::IS_SERVER ? "Server: " : "Client: ")
+#define ENDPOINT                                                         \
+  (unacked_packets_.perspective() == Perspective::IS_SERVER ? "Server: " \
+                                                            : "Client: ")
 
 QuicSentPacketManager::QuicSentPacketManager(
     Perspective perspective,
@@ -76,15 +77,19 @@
     QuicConnectionStats* stats,
     CongestionControlType congestion_control_type,
     LossDetectionType loss_type)
-    : unacked_packets_(),
-      perspective_(perspective),
+    : unacked_packets_(perspective),
       clock_(clock),
       stats_(stats),
       debug_delegate_(nullptr),
       network_change_visitor_(nullptr),
       initial_congestion_window_(kInitialCongestionWindow),
-      loss_algorithm_(&general_loss_algorithm_),
+      loss_algorithm_(
+          unacked_packets_.use_uber_loss_algorithm()
+              ? dynamic_cast<LossDetectionInterface*>(&uber_loss_algorithm_)
+              : dynamic_cast<LossDetectionInterface*>(
+                    &general_loss_algorithm_)),
       general_loss_algorithm_(loss_type),
+      uber_loss_algorithm_(loss_type),
       n_connection_simulation_(false),
       consecutive_rto_count_(0),
       consecutive_tlp_count_(0),
@@ -107,20 +112,17 @@
       delayed_ack_time_(
           QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs)),
       rtt_updated_(false),
-      acked_packets_iter_(last_ack_frame_.packets.rbegin()),
-      aggregate_acked_stream_frames_(
-          GetQuicReloadableFlag(quic_aggregate_acked_stream_frames_2)),
-      fix_mark_for_loss_retransmission_(
-          GetQuicReloadableFlag(quic_fix_mark_for_loss_retransmission)) {
+      acked_packets_iter_(last_ack_frame_.packets.rbegin()) {
   SetSendAlgorithm(congestion_control_type);
 }
 
 QuicSentPacketManager::~QuicSentPacketManager() {}
 
 void QuicSentPacketManager::SetFromConfig(const QuicConfig& config) {
+  const Perspective perspective = unacked_packets_.perspective();
   if (config.HasReceivedInitialRoundTripTimeUs() &&
       config.ReceivedInitialRoundTripTimeUs() > 0) {
-    if (!config.HasClientSentConnectionOption(kNRTT, perspective_)) {
+    if (!config.HasClientSentConnectionOption(kNRTT, perspective)) {
       SetInitialRtt(QuicTime::Delta::FromMicroseconds(
           config.ReceivedInitialRoundTripTimeUs()));
     }
@@ -129,55 +131,54 @@
     SetInitialRtt(QuicTime::Delta::FromMicroseconds(
         config.GetInitialRoundTripTimeUsToSend()));
   }
-  if (config.HasClientSentConnectionOption(kMAD0, perspective_)) {
+  if (config.HasClientSentConnectionOption(kMAD0, perspective)) {
     rtt_stats_.set_ignore_max_ack_delay(true);
   }
-  if (config.HasClientSentConnectionOption(kMAD1, perspective_)) {
+  if (config.HasClientSentConnectionOption(kMAD1, perspective)) {
     rtt_stats_.set_initial_max_ack_delay(delayed_ack_time_);
   }
-  if (config.HasClientSentConnectionOption(kMAD2, perspective_)) {
+  if (config.HasClientSentConnectionOption(kMAD2, perspective)) {
     min_tlp_timeout_ = QuicTime::Delta::Zero();
   }
-  if (config.HasClientSentConnectionOption(kMAD3, perspective_)) {
+  if (config.HasClientSentConnectionOption(kMAD3, perspective)) {
     min_rto_timeout_ = QuicTime::Delta::Zero();
   }
-  if (config.HasClientSentConnectionOption(kMAD4, perspective_)) {
+  if (config.HasClientSentConnectionOption(kMAD4, perspective)) {
     ietf_style_tlp_ = true;
   }
-  if (config.HasClientSentConnectionOption(kMAD5, perspective_)) {
+  if (config.HasClientSentConnectionOption(kMAD5, perspective)) {
     ietf_style_2x_tlp_ = true;
   }
 
   // Configure congestion control.
-  if (config.HasClientRequestedIndependentOption(kTBBR, perspective_)) {
+  if (config.HasClientRequestedIndependentOption(kTBBR, perspective)) {
     SetSendAlgorithm(kBBR);
   }
-  if (config.HasClientRequestedIndependentOption(kRENO, perspective_)) {
+  if (config.HasClientRequestedIndependentOption(kRENO, perspective)) {
     SetSendAlgorithm(kRenoBytes);
-  } else if (config.HasClientRequestedIndependentOption(kBYTE, perspective_) ||
+  } else if (config.HasClientRequestedIndependentOption(kBYTE, perspective) ||
              (GetQuicReloadableFlag(quic_default_to_bbr) &&
-              config.HasClientRequestedIndependentOption(kQBIC,
-                                                         perspective_))) {
+              config.HasClientRequestedIndependentOption(kQBIC, perspective))) {
     SetSendAlgorithm(kCubicBytes);
   } else if (GetQuicReloadableFlag(quic_enable_pcc3) &&
-             config.HasClientRequestedIndependentOption(kTPCC, perspective_)) {
+             config.HasClientRequestedIndependentOption(kTPCC, perspective)) {
     SetSendAlgorithm(kPCC);
   }
   // Initial window.
   if (GetQuicReloadableFlag(quic_unified_iw_options)) {
-    if (config.HasClientRequestedIndependentOption(kIW03, perspective_)) {
+    if (config.HasClientRequestedIndependentOption(kIW03, perspective)) {
       initial_congestion_window_ = 3;
       send_algorithm_->SetInitialCongestionWindowInPackets(3);
     }
-    if (config.HasClientRequestedIndependentOption(kIW10, perspective_)) {
+    if (config.HasClientRequestedIndependentOption(kIW10, perspective)) {
       initial_congestion_window_ = 10;
       send_algorithm_->SetInitialCongestionWindowInPackets(10);
     }
-    if (config.HasClientRequestedIndependentOption(kIW20, perspective_)) {
+    if (config.HasClientRequestedIndependentOption(kIW20, perspective)) {
       initial_congestion_window_ = 20;
       send_algorithm_->SetInitialCongestionWindowInPackets(20);
     }
-    if (config.HasClientRequestedIndependentOption(kIW50, perspective_)) {
+    if (config.HasClientRequestedIndependentOption(kIW50, perspective)) {
       initial_congestion_window_ = 50;
       send_algorithm_->SetInitialCongestionWindowInPackets(50);
     }
@@ -185,41 +186,53 @@
 
   using_pacing_ = !FLAGS_quic_disable_pacing_for_perf_tests;
 
-  if (config.HasClientSentConnectionOption(k1CON, perspective_)) {
+  if (config.HasClientSentConnectionOption(k1CON, perspective)) {
     send_algorithm_->SetNumEmulatedConnections(1);
   }
-  if (config.HasClientSentConnectionOption(kNCON, perspective_)) {
+  if (config.HasClientSentConnectionOption(kNCON, perspective)) {
     n_connection_simulation_ = true;
   }
-  if (config.HasClientSentConnectionOption(kNTLP, perspective_)) {
+  if (config.HasClientSentConnectionOption(kNTLP, perspective)) {
     max_tail_loss_probes_ = 0;
   }
-  if (config.HasClientSentConnectionOption(k1TLP, perspective_)) {
+  if (config.HasClientSentConnectionOption(k1TLP, perspective)) {
     max_tail_loss_probes_ = 1;
   }
-  if (config.HasClientSentConnectionOption(k1RTO, perspective_)) {
+  if (config.HasClientSentConnectionOption(k1RTO, perspective)) {
     max_rto_packets_ = 1;
   }
-  if (config.HasClientSentConnectionOption(kTLPR, perspective_)) {
+  if (config.HasClientSentConnectionOption(kTLPR, perspective)) {
     enable_half_rtt_tail_loss_probe_ = true;
   }
-  if (config.HasClientSentConnectionOption(kNRTO, perspective_)) {
+  if (config.HasClientSentConnectionOption(kNRTO, perspective)) {
     use_new_rto_ = true;
   }
   // Configure loss detection.
-  if (config.HasClientRequestedIndependentOption(kTIME, perspective_)) {
-    general_loss_algorithm_.SetLossDetectionType(kTime);
+  if (config.HasClientRequestedIndependentOption(kTIME, perspective)) {
+    if (unacked_packets_.use_uber_loss_algorithm()) {
+      uber_loss_algorithm_.SetLossDetectionType(kTime);
+    } else {
+      general_loss_algorithm_.SetLossDetectionType(kTime);
+    }
   }
-  if (config.HasClientRequestedIndependentOption(kATIM, perspective_)) {
-    general_loss_algorithm_.SetLossDetectionType(kAdaptiveTime);
+  if (config.HasClientRequestedIndependentOption(kATIM, perspective)) {
+    if (unacked_packets_.use_uber_loss_algorithm()) {
+      uber_loss_algorithm_.SetLossDetectionType(kAdaptiveTime);
+    } else {
+      general_loss_algorithm_.SetLossDetectionType(kAdaptiveTime);
+    }
   }
-  if (config.HasClientRequestedIndependentOption(kLFAK, perspective_)) {
-    general_loss_algorithm_.SetLossDetectionType(kLazyFack);
+  if (config.HasClientRequestedIndependentOption(kLFAK, perspective)) {
+    if (unacked_packets_.use_uber_loss_algorithm()) {
+      uber_loss_algorithm_.SetLossDetectionType(kLazyFack);
+    } else {
+      general_loss_algorithm_.SetLossDetectionType(kLazyFack);
+    }
   }
-  if (config.HasClientSentConnectionOption(kCONH, perspective_)) {
+  if (config.HasClientSentConnectionOption(kCONH, perspective)) {
     conservative_handshake_retransmits_ = true;
   }
-  send_algorithm_->SetFromConfig(config, perspective_);
+  send_algorithm_->SetFromConfig(config, perspective);
 
   if (network_change_visitor_ != nullptr) {
     network_change_visitor_->OnCongestionChange();
@@ -257,13 +270,19 @@
   }
 }
 
+void QuicSentPacketManager::SetHandshakeConfirmed() {
+  handshake_confirmed_ = true;
+  if (unacked_packets_.use_uber_loss_algorithm()) {
+    NeuterHandshakePackets();
+  }
+}
+
 void QuicSentPacketManager::PostProcessAfterMarkingPacketHandled(
     const QuicAckFrame& ack_frame,
     QuicTime ack_receive_time,
     bool rtt_updated,
     QuicByteCount prior_bytes_in_flight) {
-  if (aggregate_acked_stream_frames_ && session_decides_what_to_write()) {
-    QUIC_RELOADABLE_FLAG_COUNT_N(quic_aggregate_acked_stream_frames_2, 1, 2);
+  if (session_decides_what_to_write()) {
     unacked_packets_.NotifyAggregatedStreamFrameAcked(
         last_ack_frame_.ack_delay_time);
   }
@@ -305,8 +324,8 @@
 
   if (debug_delegate_ != nullptr) {
     debug_delegate_->OnIncomingAck(ack_frame, ack_receive_time,
-                                   unacked_packets_.largest_acked(),
-                                   rtt_updated, GetLeastUnacked());
+                                   LargestAcked(ack_frame), rtt_updated,
+                                   GetLeastUnacked());
   }
   // Remove packets below least unacked from all_packets_acked_ and
   // last_ack_frame_.
@@ -343,7 +362,7 @@
   for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
        it != unacked_packets_.end(); ++it, ++packet_number) {
     if ((retransmission_type == ALL_UNACKED_RETRANSMISSION ||
-         it->encryption_level == ENCRYPTION_INITIAL) &&
+         it->encryption_level == ENCRYPTION_ZERO_RTT) &&
         unacked_packets_.HasRetransmittableFrames(*it)) {
       MarkForRetransmission(packet_number, retransmission_type);
     }
@@ -380,6 +399,29 @@
   }
 }
 
+void QuicSentPacketManager::NeuterHandshakePackets() {
+  DCHECK(unacked_packets_.use_uber_loss_algorithm());
+  QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked();
+  for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+       it != unacked_packets_.end(); ++it, ++packet_number) {
+    if (session_decides_what_to_write()) {
+      if (!it->retransmittable_frames.empty() &&
+          unacked_packets_.GetPacketNumberSpace(it->encryption_level) ==
+              HANDSHAKE_DATA) {
+        unacked_packets_.RemoveFromInFlight(packet_number);
+      }
+      continue;
+    }
+    if (unacked_packets_.GetPacketNumberSpace(it->encryption_level) ==
+            HANDSHAKE_DATA &&
+        unacked_packets_.HasRetransmittableFrames(*it)) {
+      pending_retransmissions_.erase(packet_number);
+      unacked_packets_.RemoveFromInFlight(packet_number);
+      unacked_packets_.RemoveRetransmittability(packet_number);
+    }
+  }
+}
+
 void QuicSentPacketManager::MarkForRetransmission(
     QuicPacketNumber packet_number,
     TransmissionType transmission_type) {
@@ -402,8 +444,7 @@
   }
 
   if (!session_decides_what_to_write()) {
-    if (fix_mark_for_loss_retransmission_ &&
-        !unacked_packets_.HasRetransmittableFrames(*transmission_info)) {
+    if (!unacked_packets_.HasRetransmittableFrames(*transmission_info)) {
       return;
     }
     if (!QuicContainsKey(pending_retransmissions_, packet_number)) {
@@ -545,15 +586,12 @@
   if (newest_transmission == packet_number) {
     // Try to aggregate acked stream frames if acked packet is not a
     // retransmission.
-    const bool fast_path = aggregate_acked_stream_frames_ &&
-                           session_decides_what_to_write() &&
+    const bool fast_path = session_decides_what_to_write() &&
                            info->transmission_type == NOT_RETRANSMISSION;
     if (fast_path) {
       unacked_packets_.MaybeAggregateAckedStreamFrame(*info, ack_delay_time);
     } else {
-      if (aggregate_acked_stream_frames_ && session_decides_what_to_write()) {
-        QUIC_RELOADABLE_FLAG_COUNT_N(quic_aggregate_acked_stream_frames_2, 2,
-                                     2);
+      if (session_decides_what_to_write()) {
         unacked_packets_.NotifyAggregatedStreamFrameAcked(ack_delay_time);
       }
       const bool new_data_acked =
@@ -818,19 +856,7 @@
                                     time);
     }
 
-    if (fix_mark_for_loss_retransmission_ ||
-        unacked_packets_.HasRetransmittableFrames(packet.packet_number)) {
-      if (fix_mark_for_loss_retransmission_) {
-        QUIC_RELOADABLE_FLAG_COUNT(quic_fix_mark_for_loss_retransmission);
-      }
-      MarkForRetransmission(packet.packet_number, LOSS_RETRANSMISSION);
-    } else {
-      // Since we will not retransmit this, we need to remove it from
-      // unacked_packets_.   This is either the current transmission of
-      // a packet whose previous transmission has been acked or a packet that
-      // has been TLP retransmitted.
-      unacked_packets_.RemoveFromInFlight(packet.packet_number);
-    }
+    MarkForRetransmission(packet.packet_number, LOSS_RETRANSMISSION);
   }
 }
 
@@ -1143,6 +1169,10 @@
       // Unackable packets are skipped earlier.
       largest_newly_acked_ = acked_packet.packet_number;
     }
+    if (unacked_packets_.use_uber_loss_algorithm()) {
+      unacked_packets_.MaybeUpdateLargestAckedOfPacketNumberSpace(
+          info->encryption_level, acked_packet.packet_number);
+    }
     MarkPacketHandled(acked_packet.packet_number, info,
                       last_ack_frame_.ack_delay_time);
   }
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h
index aad16e2..4799e1f 100644
--- a/quic/core/quic_sent_packet_manager.h
+++ b/quic/core/quic_sent_packet_manager.h
@@ -13,11 +13,10 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.h"
-#include "net/third_party/quiche/src/quic/core/congestion_control/loss_detection_interface.h"
 #include "net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.h"
 #include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h"
 #include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.h"
 #include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.proto.h"
 #include "net/third_party/quiche/src/quic/core/quic_packets.h"
 #include "net/third_party/quiche/src/quic/core/quic_pending_retransmission.h"
@@ -115,7 +114,9 @@
     return pacing_sender_.max_pacing_rate();
   }
 
-  void SetHandshakeConfirmed() { handshake_confirmed_ = true; }
+  // Set handshake_confirmed_ to true and neuter packets in HANDSHAKE packet
+  // number space.
+  void SetHandshakeConfirmed();
 
   // Requests retransmission of all unacked packets of |retransmission_type|.
   // The behavior of this method depends on the value of |retransmission_type|:
@@ -140,6 +141,8 @@
 
   // Removes the retransmittable frames from all unencrypted packets to ensure
   // they don't get retransmitted.
+  // TODO(fayang): Consider remove this function when deprecating
+  // quic_use_uber_loss_algorithm.
   void NeuterUnencryptedPackets();
 
   // Returns true if there are pending retransmissions.
@@ -473,6 +476,14 @@
   // Sets the initial RTT of the connection.
   void SetInitialRtt(QuicTime::Delta rtt);
 
+  // Called when handshake is confirmed to remove the retransmittable frames
+  // from all packets of HANDSHAKE_DATA packet number space to ensure they don't
+  // get retransmitted and will eventually be removed from unacked packets map.
+  // Only used when quic_use_uber_loss_algorithm is true. Please note, this only
+  // applies to QUIC Crypto and needs to be changed when switches to IETF QUIC
+  // with QUIC TLS.
+  void NeuterHandshakePackets();
+
   // Newly serialized retransmittable packets are added to this map, which
   // contains owning pointers to any contained frames.  If a packet is
   // retransmitted, this map will contain entries for both the old and the new
@@ -486,9 +497,6 @@
   // Pending retransmissions which have not been packetized and sent yet.
   PendingRetransmissionMap pending_retransmissions_;
 
-  // Tracks if the connection was created by the server or the client.
-  Perspective perspective_;
-
   const QuicClock* clock_;
   QuicConnectionStats* stats_;
 
@@ -499,7 +507,10 @@
   std::unique_ptr<SendAlgorithmInterface> send_algorithm_;
   // Not owned. Always points to |general_loss_algorithm_| outside of tests.
   LossDetectionInterface* loss_algorithm_;
+  // TODO(fayang): Remove general_loss_algorithm_ when deprecating
+  // quic_use_uber_loss_algorithm.
   GeneralLossAlgorithm general_loss_algorithm_;
+  UberLossAlgorithm uber_loss_algorithm_;
   bool n_connection_simulation_;
 
   // Tracks the first RTO packet.  If any packet before that packet gets acked,
@@ -572,12 +583,6 @@
   // A reverse iterator of last_ack_frame_.packets. This is reset in
   // OnAckRangeStart, and gradually moves in OnAckRange..
   PacketNumberQueue::const_reverse_iterator acked_packets_iter_;
-
-  // Latched value of quic_aggregate_acked_stream_frames_2 flag.
-  const bool aggregate_acked_stream_frames_;
-
-  // Latched value of quic_fix_mark_for_loss_retransmission flag.
-  const bool fix_mark_for_loss_retransmission_;
 };
 
 }  // namespace quic
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc
index 97e1697..6ed2078 100644
--- a/quic/core/quic_sent_packet_manager_test.cc
+++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -322,7 +322,7 @@
   StrictMock<MockSessionNotifier> notifier_;
 };
 
-INSTANTIATE_TEST_CASE_P(Tests, QuicSentPacketManagerTest, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Tests, QuicSentPacketManagerTest, testing::Bool());
 
 TEST_P(QuicSentPacketManagerTest, IsUnacked) {
   VerifyUnackedPackets(nullptr, 0);
@@ -504,11 +504,9 @@
   if (manager_.session_decides_what_to_write()) {
     // Frames in all packets are acked.
     EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
-    if (GetQuicReloadableFlag(quic_fix_mark_for_loss_retransmission)) {
-      // Notify session that stream frame in packet 2 gets lost although it is
-      // not outstanding.
-      EXPECT_CALL(notifier_, OnFrameLost(_)).Times(1);
-    }
+    // Notify session that stream frame in packet 2 gets lost although it is
+    // not outstanding.
+    EXPECT_CALL(notifier_, OnFrameLost(_)).Times(1);
   }
   manager_.OnAckFrameStart(QuicPacketNumber(5), QuicTime::Delta::Infinite(),
                            clock_.Now());
@@ -516,8 +514,7 @@
   manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 
-  if (manager_.session_decides_what_to_write() &&
-      GetQuicReloadableFlag(quic_fix_mark_for_loss_retransmission)) {
+  if (manager_.session_decides_what_to_write()) {
     uint64_t unacked[] = {2};
     VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   } else {
@@ -629,11 +626,9 @@
   if (manager_.session_decides_what_to_write()) {
     // Frames in all packets are acked.
     EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
-    if (GetQuicReloadableFlag(quic_fix_mark_for_loss_retransmission)) {
-      // Notify session that stream frame in packet 2 gets lost although it is
-      // not outstanding.
-      EXPECT_CALL(notifier_, OnFrameLost(_)).Times(1);
-    }
+    // Notify session that stream frame in packet 2 gets lost although it is
+    // not outstanding.
+    EXPECT_CALL(notifier_, OnFrameLost(_)).Times(1);
   }
   manager_.OnAckFrameStart(QuicPacketNumber(5), QuicTime::Delta::Infinite(),
                            clock_.Now());
@@ -641,8 +636,7 @@
   manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 
-  if (manager_.session_decides_what_to_write() &&
-      GetQuicReloadableFlag(quic_fix_mark_for_loss_retransmission)) {
+  if (manager_.session_decides_what_to_write()) {
     uint64_t unacked[] = {2};
     VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   } else {
@@ -873,11 +867,9 @@
   if (manager_.session_decides_what_to_write()) {
     // Frames in all packets are acked.
     EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
-    if (GetQuicReloadableFlag(quic_fix_mark_for_loss_retransmission)) {
-      // Notify session that stream frame in packets 1 and 2 get lost although
-      // they are not outstanding.
-      EXPECT_CALL(notifier_, OnFrameLost(_)).Times(2);
-    }
+    // Notify session that stream frame in packets 1 and 2 get lost although
+    // they are not outstanding.
+    EXPECT_CALL(notifier_, OnFrameLost(_)).Times(2);
   }
   manager_.OnAckFrameStart(QuicPacketNumber(5), QuicTime::Delta::Infinite(),
                            clock_.Now());
@@ -980,27 +972,12 @@
                   true, _, _, Pointwise(PacketNumberEq(), {largest_acked}), _));
   EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
   if (manager_.session_decides_what_to_write()) {
-    if (GetQuicReloadableFlag(quic_fix_mark_for_loss_retransmission)) {
-      // Although frames in packet 3 gets acked, it would be kept for another
-      // RTT.
-      EXPECT_CALL(notifier_, IsFrameOutstanding(_))
-          .WillRepeatedly(Return(true));
-    } else {
-      // Frames in packet 3 gets acked as packet 103 gets acked.
-      EXPECT_CALL(notifier_, IsFrameOutstanding(_))
-          .WillOnce(Return(true))
-          .WillOnce(Return(true))
-          .WillOnce(Return(false))
-          .WillRepeatedly(Return(true));
-    }
-    if (GetQuicReloadableFlag(quic_fix_mark_for_loss_retransmission)) {
-      // Packets [1, 102] are lost, although stream frame in packet 3 is not
-      // outstanding.
-      EXPECT_CALL(notifier_, OnFrameLost(_)).Times(102);
-    } else {
-      // Packets 1, 2 and [4, 102] are lost.
-      EXPECT_CALL(notifier_, OnFrameLost(_)).Times(101);
-    }
+    // Although frames in packet 3 gets acked, it would be kept for another
+    // RTT.
+    EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(true));
+    // Packets [1, 102] are lost, although stream frame in packet 3 is not
+    // outstanding.
+    EXPECT_CALL(notifier_, OnFrameLost(_)).Times(102);
   }
   manager_.OnAckFrameStart(QuicPacketNumber(103), QuicTime::Delta::Infinite(),
                            clock_.Now());
@@ -1363,27 +1340,10 @@
                 OnPacketLoss(QuicPacketNumber(i), LOSS_RETRANSMISSION, _));
   }
   if (manager_.session_decides_what_to_write()) {
-    if (GetQuicReloadableFlag(quic_fix_mark_for_loss_retransmission)) {
-      EXPECT_CALL(notifier_, IsFrameOutstanding(_))
-          .WillRepeatedly(Return(true));
-    } else {
-      EXPECT_CALL(notifier_, IsFrameOutstanding(_))
-          .WillOnce(Return(true))
-          // This is used for QUIC_BUG_IF in MarkForRetransmission, which is not
-          // ideal.
-          .WillOnce(Return(true))
-          .WillOnce(Return(false))
-          .WillRepeatedly(Return(true));
-    }
-    if (GetQuicReloadableFlag(quic_fix_mark_for_loss_retransmission)) {
-      // Packets [1, 99] are considered as lost, although stream frame in packet
-      // 2 is not outstanding.
-      EXPECT_CALL(notifier_, OnFrameLost(_)).Times(99);
-    } else {
-      // Packets [1, 99] are considered as lost, but packets 2 does not have
-      // retransmittable frames as packet 102 is acked.
-      EXPECT_CALL(notifier_, OnFrameLost(_)).Times(98);
-    }
+    EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(true));
+    // Packets [1, 99] are considered as lost, although stream frame in packet
+    // 2 is not outstanding.
+    EXPECT_CALL(notifier_, OnFrameLost(_)).Times(99);
   }
   manager_.OnAckFrameStart(QuicPacketNumber(102), QuicTime::Delta::Zero(),
                            clock_.Now());
@@ -1492,27 +1452,10 @@
                                 /*lost_packets=*/Not(IsEmpty())));
   EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
   if (manager_.session_decides_what_to_write()) {
-    if (GetQuicReloadableFlag(quic_fix_mark_for_loss_retransmission)) {
-      EXPECT_CALL(notifier_, IsFrameOutstanding(_))
-          .WillRepeatedly(Return(true));
-    } else {
-      EXPECT_CALL(notifier_, IsFrameOutstanding(_))
-          .WillOnce(Return(true))
-          // This is used for QUIC_BUG_IF in MarkForRetransmission, which is not
-          // ideal.
-          .WillOnce(Return(true))
-          .WillOnce(Return(false))
-          .WillRepeatedly(Return(true));
-    }
-    if (GetQuicReloadableFlag(quic_fix_mark_for_loss_retransmission)) {
-      // Packets [1, 99] are considered as lost, although stream frame in packet
-      // 2 is not outstanding.
-      EXPECT_CALL(notifier_, OnFrameLost(_)).Times(99);
-    } else {
-      // Packets [1, 99] are considered as lost, but packets 2 does not have
-      // retransmittable frames as packet 102 is acked.
-      EXPECT_CALL(notifier_, OnFrameLost(_)).Times(98);
-    }
+    EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(true));
+    // Packets [1, 99] are considered as lost, although stream frame in packet
+    // 2 is not outstanding.
+    EXPECT_CALL(notifier_, OnFrameLost(_)).Times(99);
   }
   manager_.OnAckFrameStart(QuicPacketNumber(102), QuicTime::Delta::Zero(),
                            clock_.Now());
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index b5e88ff..2eb67e7 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -151,6 +151,10 @@
   handler.stream->OnStreamFrame(frame);
 }
 
+void QuicSession::OnCryptoFrame(const QuicCryptoFrame& frame) {
+  GetMutableCryptoStream()->OnCryptoFrame(frame);
+}
+
 bool QuicSession::OnStopSendingFrame(const QuicStopSendingFrame& frame) {
   // We are not version 99. In theory, if not in version 99 then the framer
   // could not call OnStopSending... This is just a check that is good when
@@ -231,7 +235,6 @@
       static_cast<quic::QuicRstStreamErrorCode>(frame.application_error_code),
       stream->stream_bytes_written(),
       /*close_write_side_only=*/true);
-  stream->set_rst_sent(true);
 
   return true;
 }
@@ -585,16 +588,13 @@
       // Send a RST_STREAM frame plus, if version 99, an IETF
       // QUIC STOP_SENDING frame. Both sre sent to emulate
       // the two-way close that Google QUIC's RST_STREAM does.
-      QuicConnection::ScopedPacketFlusher* flusher =
-          (connection_->transport_version() == QUIC_VERSION_99)
-              ? new QuicConnection::ScopedPacketFlusher(
-                    connection(), QuicConnection::SEND_ACK_IF_QUEUED)
-              : nullptr;
-      control_frame_manager_.WriteOrBufferRstStreamStopSending(id, error,
-                                                               bytes_written);
-      if (flusher) {
-        delete flusher;
-        flusher = nullptr;
+      if (connection_->transport_version() == QUIC_VERSION_99) {
+        QuicConnection::ScopedPacketFlusher flusher(
+            connection(), QuicConnection::SEND_ACK_IF_QUEUED);
+        control_frame_manager_.WriteOrBufferRstStream(id, error, bytes_written);
+        control_frame_manager_.WriteOrBufferStopSending(error, id);
+      } else {
+        control_frame_manager_.WriteOrBufferRstStream(id, error, bytes_written);
       }
     }
     connection_->OnStreamReset(id, error);
@@ -603,17 +603,20 @@
     OnStreamDoneWaitingForAcks(id);
     return;
   }
-  if (close_write_side_only) {
-    DCHECK_EQ(QUIC_VERSION_99, connection_->transport_version());
-    DynamicStreamMap::iterator it = dynamic_stream_map_.find(id);
-    if (it != dynamic_stream_map_.end()) {
-      QuicStream* stream = it->second.get();
-      if (stream) {
-        stream->CloseWriteSide();
-      }
-    }
-  } else {
+
+  if (!close_write_side_only) {
     CloseStreamInner(id, true);
+    return;
+  }
+  DCHECK_EQ(QUIC_VERSION_99, connection_->transport_version());
+
+  DynamicStreamMap::iterator it = dynamic_stream_map_.find(id);
+  if (it != dynamic_stream_map_.end()) {
+    QuicStream* stream = it->second.get();
+    if (stream) {
+      stream->set_rst_sent(true);
+      stream->CloseWriteSide();
+    }
   }
 }
 
@@ -1366,6 +1369,10 @@
     OnMessageAcked(frame.message_frame->message_id);
     return true;
   }
+  if (frame.type == CRYPTO_FRAME) {
+    return GetMutableCryptoStream()->OnCryptoFrameAcked(*frame.crypto_frame,
+                                                        ack_delay_time);
+  }
   if (frame.type != STREAM_FRAME) {
     return control_frame_manager_.OnControlFrameAcked(frame);
   }
@@ -1373,9 +1380,10 @@
   QuicStream* stream = GetStream(frame.stream_frame.stream_id);
   // Stream can already be reset when sent frame gets acked.
   if (stream != nullptr) {
+    QuicByteCount newly_acked_length = 0;
     new_stream_data_acked = stream->OnStreamFrameAcked(
         frame.stream_frame.offset, frame.stream_frame.data_length,
-        frame.stream_frame.fin, ack_delay_time);
+        frame.stream_frame.fin, ack_delay_time, &newly_acked_length);
     if (!stream->HasPendingRetransmission()) {
       streams_with_pending_retransmission_.erase(stream->id());
     }
@@ -1402,6 +1410,10 @@
     OnMessageLost(frame.message_frame->message_id);
     return;
   }
+  if (frame.type == CRYPTO_FRAME) {
+    GetMutableCryptoStream()->OnCryptoFrameLost(frame.crypto_frame);
+    return;
+  }
   if (frame.type != STREAM_FRAME) {
     control_frame_manager_.OnControlFrameLost(frame);
     return;
@@ -1431,6 +1443,10 @@
       // Do not retransmit MESSAGE frames.
       continue;
     }
+    if (frame.type == CRYPTO_FRAME) {
+      GetMutableCryptoStream()->RetransmitData(frame.crypto_frame);
+      continue;
+    }
     if (frame.type != STREAM_FRAME) {
       if (!control_frame_manager_.RetransmitControlFrame(frame)) {
         break;
@@ -1451,6 +1467,11 @@
   if (frame.type == MESSAGE_FRAME) {
     return false;
   }
+  if (frame.type == CRYPTO_FRAME) {
+    return GetCryptoStream()->IsFrameOutstanding(
+        frame.crypto_frame->level, frame.crypto_frame->offset,
+        frame.crypto_frame->data_length);
+  }
   if (frame.type != STREAM_FRAME) {
     return control_frame_manager_.IsControlFrameOutstanding(frame);
   }
@@ -1495,21 +1516,27 @@
                                   QuicStreamOffset offset,
                                   QuicByteCount data_length,
                                   QuicDataWriter* writer) {
-  QUIC_BUG << "QuicSession::WriteCryptoData is unimplemented";
-  return false;
+  return GetMutableCryptoStream()->WriteCryptoFrame(level, offset, data_length,
+                                                    writer);
 }
 
 QuicUint128 QuicSession::GetStatelessResetToken() const {
-  if (!QuicConnectionIdSupportsVariableLength(perspective())) {
-    return QuicConnectionIdToUInt64(connection_->connection_id());
-  }
   return QuicUtils::GenerateStatelessResetToken(connection_->connection_id());
 }
 
 bool QuicSession::RetransmitLostData() {
   QuicConnection::ScopedPacketFlusher retransmission_flusher(
       connection_, QuicConnection::SEND_ACK_IF_QUEUED);
-  if (QuicContainsKey(
+  // Retransmit crypto data first.
+  bool uses_crypto_frames = connection_->transport_version() >= QUIC_VERSION_47;
+  QuicCryptoStream* crypto_stream = GetMutableCryptoStream();
+  if (uses_crypto_frames && crypto_stream->HasPendingCryptoRetransmission()) {
+    SetTransmissionType(HANDSHAKE_RETRANSMISSION);
+    crypto_stream->WritePendingCryptoRetransmission();
+  }
+  // Retransmit crypto data in stream 1 frames (version < 47).
+  if (!uses_crypto_frames &&
+      QuicContainsKey(
           streams_with_pending_retransmission_,
           QuicUtils::GetCryptoStreamId(connection_->transport_version()))) {
     SetTransmissionType(HANDSHAKE_RETRANSMISSION);
@@ -1579,7 +1606,7 @@
   connection_->SetTransmissionType(type);
 }
 
-MessageResult QuicSession::SendMessage(QuicStringPiece message) {
+MessageResult QuicSession::SendMessage(QuicMemSliceSpan message) {
   if (!IsEncryptionEstablished()) {
     return {MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED, 0};
   }
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index 23778e7..cb0f0a7 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -77,7 +77,7 @@
     // ENCRYPTION_REESTABLISHED indicates that a client hello was rejected by
     // the server and thus the encryption key has been updated. Therefore the
     // connection should resend any packets that were sent under
-    // ENCRYPTION_INITIAL. (Client only.)
+    // ENCRYPTION_ZERO_RTT. (Client only.)
     ENCRYPTION_REESTABLISHED,
     // HANDSHAKE_CONFIRMED, in a client, indicates the server has accepted
     // our handshake. In a server it indicates that a full, valid client hello
@@ -99,6 +99,7 @@
 
   // QuicConnectionVisitorInterface methods:
   void OnStreamFrame(const QuicStreamFrame& frame) override;
+  void OnCryptoFrame(const QuicCryptoFrame& frame) override;
   void OnRstStream(const QuicRstStreamFrame& frame) override;
   void OnGoAway(const QuicGoAwayFrame& frame) override;
   void OnMessageReceived(QuicStringPiece message) override;
@@ -165,11 +166,17 @@
                                       QuicStreamOffset offset,
                                       StreamSendingState state);
 
-  // Called by application to send |message|. Returns the message result which
-  // includes the message status and message ID (valid if the write succeeds).
-  // SendMessage flushes a message packet even it is not full. If the
-  // application wants to bundle other data in the same packet, please consider
-  // adding a packet flusher around the SendMessage and/or WritevData calls.
+  // Called by application to send |message|. Data copy can be avoided if
+  // |message| is provided in reference counted memory.
+  // Please note, |message| provided in reference counted memory would be moved
+  // internally when message is successfully sent. Thereafter, it would be
+  // undefined behavior if callers try to access the slices through their own
+  // copy of the span object.
+  // Returns the message result which includes the message status and message ID
+  // (valid if the write succeeds). SendMessage flushes a message packet even it
+  // is not full. If the application wants to bundle other data in the same
+  // packet, please consider adding a packet flusher around the SendMessage
+  // and/or WritevData calls.
   //
   // OnMessageAcked and OnMessageLost are called when a particular message gets
   // acked or lost.
@@ -179,7 +186,7 @@
   // blocked. In this case the caller can retry sending message again when
   // connection becomes available, for example after getting OnCanWrite()
   // callback.
-  MessageResult SendMessage(QuicStringPiece message);
+  MessageResult SendMessage(QuicMemSliceSpan message);
 
   // Called when message with |message_id| gets acked.
   virtual void OnMessageAcked(QuicMessageId message_id);
@@ -381,6 +388,17 @@
     return num_locally_closed_incoming_streams_highest_offset_;
   }
 
+  // Does actual work of sending reset-stream or reset-stream&stop-sending
+  // If the connection is not version 99/IETF QUIC, will always send a
+  // RESET_STREAM and close_write_side_only is ignored. If the connection is
+  // IETF QUIC/Version 99 then will send a RESET_STREAM and STOP_SENDING if
+  // close_write_side_only is false, just a RESET_STREAM if
+  // close_write_side_only is true.
+  virtual void SendRstStreamInner(QuicStreamId id,
+                                  QuicRstStreamErrorCode error,
+                                  QuicStreamOffset bytes_written,
+                                  bool close_write_side_only);
+
  protected:
   using StaticStreamMap = QuicSmallMap<QuicStreamId, QuicStream*, 2>;
 
@@ -571,17 +589,6 @@
   // Closes the pending stream |stream_id| before it has been created.
   void ClosePendingStream(QuicStreamId stream_id);
 
-  // Does actual work of sending reset-stream or reset-stream&stop-sending
-  // If the connection is not version 99/IETF QUIC, will always send a
-  // RESET_STREAM and close_write_side_only is ignored. If the connection is
-  // IETF QUIC/Version 99 then will send a RESET_STREAM and STOP_SENDING if
-  // close_write_side_only is false, just a RESET_STREAM if
-  // close_write_side_only is true.
-  void SendRstStreamInner(QuicStreamId id,
-                          QuicRstStreamErrorCode error,
-                          QuicStreamOffset bytes_written,
-                          bool close_write_side_only);
-
   // Keep track of highest received byte offset of locally closed streams, while
   // waiting for a definitive final highest offset from the peer.
   std::map<QuicStreamId, QuicStreamOffset>
diff --git a/quic/core/quic_session_test.cc b/quic/core/quic_session_test.cc
index 6bf26ab..ddd5a2b 100644
--- a/quic/core/quic_session_test.cc
+++ b/quic/core/quic_session_test.cc
@@ -98,6 +98,7 @@
   }
 
   MOCK_METHOD0(OnCanWrite, void());
+  bool HasPendingCryptoRetransmission() override { return false; }
 
   MOCK_CONST_METHOD0(HasPendingRetransmission, bool());
 
@@ -191,10 +192,11 @@
       return nullptr;
     }
 
-    TestStream* stream = new TestStream(
-        id, this,
-        DetermineStreamType(id, connection()->transport_version(),
-                            /*is_incoming=*/true, BIDIRECTIONAL));
+    TestStream* stream =
+        new TestStream(id, this,
+                       DetermineStreamType(
+                           id, connection()->transport_version(), perspective(),
+                           /*is_incoming=*/true, BIDIRECTIONAL));
     ActivateStream(QuicWrapUnique(stream));
     ++num_incoming_streams_created_;
     return stream;
@@ -202,10 +204,11 @@
 
   TestStream* CreateIncomingStream(PendingStream pending) override {
     QuicStreamId id = pending.id();
-    TestStream* stream = new TestStream(
-        std::move(pending),
-        DetermineStreamType(id, connection()->transport_version(),
-                            /*is_incoming=*/true, BIDIRECTIONAL));
+    TestStream* stream =
+        new TestStream(std::move(pending),
+                       DetermineStreamType(
+                           id, connection()->transport_version(), perspective(),
+                           /*is_incoming=*/true, BIDIRECTIONAL));
     ActivateStream(QuicWrapUnique(stream));
     ++num_incoming_streams_created_;
     return stream;
@@ -335,7 +338,8 @@
 
   void CloseStream(QuicStreamId id) {
     if (session_.connection()->transport_version() == QUIC_VERSION_99 &&
-        QuicUtils::GetStreamType(id, session_.IsIncomingStream(id)) ==
+        QuicUtils::GetStreamType(id, session_.perspective(),
+                                 session_.IsIncomingStream(id)) ==
             READ_UNIDIRECTIONAL) {
       // Verify reset is not sent for READ_UNIDIRECTIONAL streams.
       EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
@@ -446,9 +450,9 @@
   QuicFramer client_framer_;
 };
 
-INSTANTIATE_TEST_CASE_P(Tests,
-                        QuicSessionTestServer,
-                        ::testing::ValuesIn(AllSupportedVersions()));
+INSTANTIATE_TEST_SUITE_P(Tests,
+                         QuicSessionTestServer,
+                         ::testing::ValuesIn(AllSupportedVersions()));
 
 TEST_P(QuicSessionTestServer, PeerAddress) {
   EXPECT_EQ(QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort),
@@ -456,7 +460,7 @@
 }
 
 TEST_P(QuicSessionTestServer, SelfAddress) {
-  EXPECT_EQ(QuicSocketAddress(), session_.self_address());
+  EXPECT_TRUE(session_.self_address().IsInitialized());
 }
 
 TEST_P(QuicSessionTestServer, DontCallOnWriteBlockedForDisconnectedConnection) {
@@ -913,8 +917,6 @@
   MockPacketWriter* writer = static_cast<MockPacketWriter*>(
       QuicConnectionPeer::GetWriter(session_.connection()));
   EXPECT_CALL(*writer, IsWriteBlocked()).WillRepeatedly(Return(true));
-  EXPECT_CALL(*writer, IsWriteBlockedDataBuffered())
-      .WillRepeatedly(Return(true));
   EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)).Times(0);
 
   TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
@@ -1237,6 +1239,12 @@
 }
 
 TEST_P(QuicSessionTestServer, HandshakeUnblocksFlowControlBlockedCryptoStream) {
+  if (GetParam().transport_version >= QUIC_VERSION_46) {
+    // QUIC version 46 onwards uses CRYPTO frames for the handshake, so this
+    // test doesn't make sense for those versions since CRYPTO frames aren't
+    // flow controlled.
+    return;
+  }
   // Test that if the crypto stream is flow control blocked, then if the SHLO
   // contains a larger send window offset, the stream becomes unblocked.
   session_.set_writev_consumes_all_data(true);
@@ -1290,19 +1298,11 @@
   const QuicStreamOffset kByteOffset =
       1 + kInitialSessionFlowControlWindowForTest / 2;
 
-  if (transport_version() == QUIC_VERSION_99) {
-    // Two more control frames than in Google QUIC, one is the STOP_SENDING
-    // frame, the other is the RST_STREAM generated in response.
-    EXPECT_CALL(*connection_, SendControlFrame(_))
-        .Times(4)
-        .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame));
-    EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _)).Times(2);
-  } else {
-    EXPECT_CALL(*connection_, SendControlFrame(_))
-        .Times(2)
-        .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame));
-    EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _));
-  }
+  EXPECT_CALL(*connection_, SendControlFrame(_))
+      .Times(2)
+      .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame));
+  EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _));
+
   QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream->id(),
                                QUIC_STREAM_CANCELLED, kByteOffset);
   session_.OnRstStream(rst_frame);
@@ -1624,9 +1624,9 @@
   QuicSessionTestClient() : QuicSessionTestBase(Perspective::IS_CLIENT) {}
 };
 
-INSTANTIATE_TEST_CASE_P(Tests,
-                        QuicSessionTestClient,
-                        ::testing::ValuesIn(AllSupportedVersions()));
+INSTANTIATE_TEST_SUITE_P(Tests,
+                         QuicSessionTestClient,
+                         ::testing::ValuesIn(AllSupportedVersions()));
 
 TEST_P(QuicSessionTestClient, AvailableBidirectionalStreamsClient) {
   ASSERT_TRUE(session_.GetOrCreateDynamicStream(
@@ -1741,20 +1741,18 @@
 
   QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream2->id(),
                                QUIC_STREAM_CANCELLED, 1234);
+  // Just for the RST_STREAM
+  EXPECT_CALL(*connection_, SendControlFrame(_))
+      .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame));
   if (transport_version() == QUIC_VERSION_99) {
-    // Once for the RST_STREAM, once for the STOP_SENDING
-    EXPECT_CALL(*connection_, SendControlFrame(_))
-        .Times(3)
-        .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame));
-    EXPECT_CALL(*connection_, OnStreamReset(stream2->id(), _)).Times(2);
+    EXPECT_CALL(*connection_,
+                OnStreamReset(stream2->id(), QUIC_STREAM_CANCELLED));
   } else {
-    // Just for the RST_STREAM
-    EXPECT_CALL(*connection_, SendControlFrame(_))
-        .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame));
     EXPECT_CALL(*connection_,
                 OnStreamReset(stream2->id(), QUIC_RST_ACKNOWLEDGEMENT));
   }
   stream2->OnStreamReset(rst_frame);
+
   if (transport_version() == QUIC_VERSION_99) {
     // The test is predicated on the stream being fully closed. For V99, the
     // RST_STREAM only does one side (the read side from the perspective of the
@@ -1810,11 +1808,18 @@
 
   // Lost data on cryption stream, streams 2 and 4.
   EXPECT_CALL(*stream4, HasPendingRetransmission()).WillOnce(Return(true));
-  EXPECT_CALL(*crypto_stream, HasPendingRetransmission())
-      .WillOnce(Return(true));
+  if (connection_->transport_version() < QUIC_VERSION_47) {
+    EXPECT_CALL(*crypto_stream, HasPendingRetransmission())
+        .WillOnce(Return(true));
+  }
   EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(true));
   session_.OnFrameLost(QuicFrame(frame3));
-  session_.OnFrameLost(QuicFrame(frame1));
+  if (connection_->transport_version() < QUIC_VERSION_47) {
+    session_.OnFrameLost(QuicFrame(frame1));
+  } else {
+    QuicCryptoFrame crypto_frame(ENCRYPTION_NONE, 0, 1300);
+    session_.OnFrameLost(QuicFrame(&crypto_frame));
+  }
   session_.OnFrameLost(QuicFrame(frame2));
   EXPECT_TRUE(session_.WillingAndAbleToWrite());
 
@@ -1826,9 +1831,11 @@
   // stream go first.
   // Do not check congestion window when crypto stream has lost data.
   EXPECT_CALL(*send_algorithm, CanSend(_)).Times(0);
-  EXPECT_CALL(*crypto_stream, OnCanWrite());
-  EXPECT_CALL(*crypto_stream, HasPendingRetransmission())
-      .WillOnce(Return(false));
+  if (connection_->transport_version() < QUIC_VERSION_47) {
+    EXPECT_CALL(*crypto_stream, OnCanWrite());
+    EXPECT_CALL(*crypto_stream, HasPendingRetransmission())
+        .WillOnce(Return(false));
+  }
   // Check congestion window for non crypto streams.
   EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true));
   EXPECT_CALL(*stream4, OnCanWrite());
@@ -1962,8 +1969,11 @@
 TEST_P(QuicSessionTestServer, SendMessage) {
   // Cannot send message when encryption is not established.
   EXPECT_FALSE(session_.IsCryptoHandshakeConfirmed());
+  quic::QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
   EXPECT_EQ(MessageResult(MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED, 0),
-            session_.SendMessage(""));
+            session_.SendMessage(
+                MakeSpan(connection_->helper()->GetStreamSendBufferAllocator(),
+                         "", &storage)));
 
   // Finish handshake.
   CryptoHandshakeMessage handshake_message;
@@ -1971,23 +1981,29 @@
   EXPECT_TRUE(session_.IsCryptoHandshakeConfirmed());
 
   QuicStringPiece message;
-  QuicMessageFrame frame(1, message);
   EXPECT_CALL(*connection_, SendMessage(1, _))
       .WillOnce(Return(MESSAGE_STATUS_SUCCESS));
   EXPECT_EQ(MessageResult(MESSAGE_STATUS_SUCCESS, 1),
-            session_.SendMessage(message));
+            session_.SendMessage(
+                MakeSpan(connection_->helper()->GetStreamSendBufferAllocator(),
+                         message, &storage)));
   // Verify message_id increases.
   EXPECT_CALL(*connection_, SendMessage(2, _))
       .WillOnce(Return(MESSAGE_STATUS_TOO_LARGE));
   EXPECT_EQ(MessageResult(MESSAGE_STATUS_TOO_LARGE, 0),
-            session_.SendMessage(message));
+            session_.SendMessage(
+                MakeSpan(connection_->helper()->GetStreamSendBufferAllocator(),
+                         message, &storage)));
   // Verify unsent message does not consume a message_id.
   EXPECT_CALL(*connection_, SendMessage(2, _))
       .WillOnce(Return(MESSAGE_STATUS_SUCCESS));
-  QuicMessageFrame frame2(2, message);
   EXPECT_EQ(MessageResult(MESSAGE_STATUS_SUCCESS, 2),
-            session_.SendMessage(message));
+            session_.SendMessage(
+                MakeSpan(connection_->helper()->GetStreamSendBufferAllocator(),
+                         message, &storage)));
 
+  QuicMessageFrame frame(1);
+  QuicMessageFrame frame2(2);
   EXPECT_FALSE(session_.IsFrameOutstanding(QuicFrame(&frame)));
   EXPECT_FALSE(session_.IsFrameOutstanding(QuicFrame(&frame2)));
 
@@ -2313,7 +2329,7 @@
   QuicStreamId stream_id = stream->id();
   QuicStopSendingFrame frame(1, stream_id, 123);
   EXPECT_CALL(*stream, OnStopSending(123));
-  // Expect a reset to come back out.xxxxx
+  // Expect a reset to come back out.
   EXPECT_CALL(*connection_, SendControlFrame(_));
   EXPECT_CALL(
       *connection_,
diff --git a/quic/core/quic_stream.cc b/quic/core/quic_stream.cc
index 6c8ea8d..70af656 100644
--- a/quic/core/quic_stream.cc
+++ b/quic/core/quic_stream.cc
@@ -23,12 +23,6 @@
 
 namespace {
 
-struct iovec MakeIovec(QuicStringPiece data) {
-  struct iovec iov = {const_cast<char*>(data.data()),
-                      static_cast<size_t>(data.size())};
-  return iov;
-}
-
 size_t GetInitialStreamFlowControlWindowToSend(QuicSession* session) {
   return session->config()->GetInitialStreamFlowControlWindowToSend();
 }
@@ -236,20 +230,16 @@
       stream_contributes_to_connection_flow_control_(true),
       busy_counter_(0),
       add_random_padding_after_fin_(false),
-      ack_listener_(nullptr),
       send_buffer_(
           session->connection()->helper()->GetStreamSendBufferAllocator()),
       buffered_data_threshold_(GetQuicFlag(FLAGS_quic_buffered_data_threshold)),
       is_static_(is_static),
       deadline_(QuicTime::Zero()),
       type_(session->connection()->transport_version() == QUIC_VERSION_99
-                ? QuicUtils::GetStreamType(id_, session->IsIncomingStream(id_))
+                ? QuicUtils::GetStreamType(id_,
+                                           perspective_,
+                                           session->IsIncomingStream(id_))
                 : type) {
-  if (session->connection()->transport_version() == QUIC_VERSION_99) {
-    DCHECK_EQ(type,
-              QuicUtils::GetStreamType(id_, session->IsIncomingStream(id_)))
-        << id_;
-  }
   if (type_ == WRITE_UNIDIRECTIONAL) {
     set_fin_received(true);
     CloseReadSide();
@@ -453,7 +443,7 @@
   // Do not respect buffered data upper limit as WriteOrBufferData guarantees
   // all data to be consumed.
   if (data.length() > 0) {
-    struct iovec iov(MakeIovec(data));
+    struct iovec iov(QuicUtils::MakeIovec(data));
     QuicStreamOffset offset = send_buffer_.stream_offset();
     if (kMaxStreamLength - offset < data.length()) {
       QUIC_BUG << "Write too many data via stream " << id_;
@@ -783,13 +773,14 @@
 bool QuicStream::OnStreamFrameAcked(QuicStreamOffset offset,
                                     QuicByteCount data_length,
                                     bool fin_acked,
-                                    QuicTime::Delta ack_delay_time) {
+                                    QuicTime::Delta ack_delay_time,
+                                    QuicByteCount* newly_acked_length) {
   QUIC_DVLOG(1) << ENDPOINT << "stream " << id_ << " Acking "
                 << "[" << offset << ", " << offset + data_length << "]"
                 << " fin = " << fin_acked;
-  QuicByteCount newly_acked_length = 0;
+  *newly_acked_length = 0;
   if (!send_buffer_.OnStreamDataAcked(offset, data_length,
-                                      &newly_acked_length)) {
+                                      newly_acked_length)) {
     CloseConnectionWithDetails(QUIC_INTERNAL_ERROR,
                                "Trying to ack unsent data.");
     return false;
@@ -801,7 +792,7 @@
   }
   // Indicates whether ack listener's OnPacketAcked should be called.
   const bool new_data_acked =
-      newly_acked_length > 0 || (fin_acked && fin_outstanding_);
+      *newly_acked_length > 0 || (fin_acked && fin_outstanding_);
   if (fin_acked) {
     fin_outstanding_ = false;
     fin_lost_ = false;
@@ -809,9 +800,6 @@
   if (!IsWaitingForAcks()) {
     session_->OnStreamDoneWaitingForAcks(id_);
   }
-  if (ack_listener_ != nullptr && new_data_acked) {
-    ack_listener_->OnPacketAcked(newly_acked_length, ack_delay_time);
-  }
   return new_data_acked;
 }
 
@@ -822,9 +810,6 @@
   if (fin_retransmitted) {
     fin_lost_ = false;
   }
-  if (ack_listener_ != nullptr) {
-    ack_listener_->OnPacketRetransmitted(data_length);
-  }
 }
 
 void QuicStream::OnStreamFrameLost(QuicStreamOffset offset,
diff --git a/quic/core/quic_stream.h b/quic/core/quic_stream.h
index 13a6b2c..fe0a794 100644
--- a/quic/core/quic_stream.h
+++ b/quic/core/quic_stream.h
@@ -286,12 +286,13 @@
                        QuicDataWriter* writer);
 
   // Called when data [offset, offset + data_length) is acked. |fin_acked|
-  // indicates whether the fin is acked. Returns true if any new stream data
-  // (including fin) gets acked.
+  // indicates whether the fin is acked. Returns true and updates
+  // |newly_acked_length| if any new stream data (including fin) gets acked.
   virtual bool OnStreamFrameAcked(QuicStreamOffset offset,
                                   QuicByteCount data_length,
                                   bool fin_acked,
-                                  QuicTime::Delta ack_delay_time);
+                                  QuicTime::Delta ack_delay_time,
+                                  QuicByteCount* newly_acked_length);
 
   // Called when data [offset, offset + data_length) was retransmitted.
   // |fin_retransmitted| indicates whether fin was retransmitted.
@@ -413,11 +414,6 @@
     stream_contributes_to_connection_flow_control_ = false;
   }
 
-  void set_ack_listener(
-      QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
-    ack_listener_ = std::move(ack_listener);
-  }
-
   const QuicIntervalSet<QuicStreamOffset>& bytes_acked() const;
 
   const QuicStreamSendBuffer& send_buffer() const { return send_buffer_; }
@@ -521,10 +517,6 @@
   // stream.
   bool add_random_padding_after_fin_;
 
-  // Ack listener of this stream, and it is notified when any of written bytes
-  // are acked.
-  QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener_;
-
   // Send buffer of this stream. Send buffer is cleaned up when data gets acked
   // or discarded.
   QuicStreamSendBuffer send_buffer_;
diff --git a/quic/core/quic_stream_id_manager_test.cc b/quic/core/quic_stream_id_manager_test.cc
index 5647d75..51799e2 100644
--- a/quic/core/quic_stream_id_manager_test.cc
+++ b/quic/core/quic_stream_id_manager_test.cc
@@ -63,6 +63,7 @@
     TestQuicStream* stream = new TestQuicStream(
         id, this,
         DetermineStreamType(id, connection()->transport_version(),
+                            perspective(),
                             /*is_incoming=*/true, BIDIRECTIONAL));
     ActivateStream(QuicWrapUnique(stream));
     return stream;
@@ -170,7 +171,7 @@
       : QuicStreamIdManagerTestBase(Perspective::IS_CLIENT) {}
 };
 
-INSTANTIATE_TEST_CASE_P(Tests, QuicStreamIdManagerTestClient, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Tests, QuicStreamIdManagerTestClient, testing::Bool());
 
 // Check that the parameters used by the stream ID manager are properly
 // initialized.
@@ -618,7 +619,7 @@
       : QuicStreamIdManagerTestBase(Perspective::IS_SERVER) {}
 };
 
-INSTANTIATE_TEST_CASE_P(Tests, QuicStreamIdManagerTestServer, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Tests, QuicStreamIdManagerTestServer, testing::Bool());
 
 // This test checks that the initialization for the maximum allowed outgoing
 // stream id is correct.
diff --git a/quic/core/quic_stream_send_buffer.cc b/quic/core/quic_stream_send_buffer.cc
index 628439a..efe6240 100644
--- a/quic/core/quic_stream_send_buffer.cc
+++ b/quic/core/quic_stream_send_buffer.cc
@@ -6,12 +6,12 @@
 
 #include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
 #include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
 #include "net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h"
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
-#include "net/third_party/quiche/src/quic/platform/api/quic_interval.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
 
 namespace quic {
diff --git a/quic/core/quic_stream_send_buffer.h b/quic/core/quic_stream_send_buffer.h
index 7b6a9c9..4bcac34 100644
--- a/quic/core/quic_stream_send_buffer.h
+++ b/quic/core/quic_stream_send_buffer.h
@@ -6,6 +6,7 @@
 #define QUICHE_QUIC_CORE_QUIC_STREAM_SEND_BUFFER_H_
 
 #include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval_set.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_iovec.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice.h"
diff --git a/quic/core/quic_stream_sequencer.cc b/quic/core/quic_stream_sequencer.cc
index cfd288d..6311dcf 100644
--- a/quic/core/quic_stream_sequencer.cc
+++ b/quic/core/quic_stream_sequencer.cc
@@ -47,12 +47,23 @@
       return;
     }
   }
+  OnFrameData(byte_offset, data_len, frame.data_buffer);
+}
+
+void QuicStreamSequencer::OnCryptoFrame(const QuicCryptoFrame& frame) {
+  ++num_frames_received_;
+  OnFrameData(frame.offset, frame.data_length, frame.data_buffer);
+}
+
+void QuicStreamSequencer::OnFrameData(QuicStreamOffset byte_offset,
+                                      size_t data_len,
+                                      const char* data_buffer) {
   const size_t previous_readable_bytes = buffered_frames_.ReadableBytes();
   size_t bytes_written;
   QuicString error_details;
   QuicErrorCode result = buffered_frames_.OnStreamData(
-      byte_offset, QuicStringPiece(frame.data_buffer, frame.data_length),
-      &bytes_written, &error_details);
+      byte_offset, QuicStringPiece(data_buffer, data_len), &bytes_written,
+      &error_details);
   if (result != QUIC_NO_ERROR) {
     QuicString details = QuicStrCat(
         "Stream ", stream_->id(), ": ", QuicErrorCodeToString(result), ": ",
diff --git a/quic/core/quic_stream_sequencer.h b/quic/core/quic_stream_sequencer.h
index dfe5e7d..3feee5e 100644
--- a/quic/core/quic_stream_sequencer.h
+++ b/quic/core/quic_stream_sequencer.h
@@ -64,6 +64,13 @@
   // buffered.
   void OnStreamFrame(const QuicStreamFrame& frame);
 
+  // If the frame is the next one we need in order to process in-order data,
+  // ProcessData will be immediately called on the crypto stream until all
+  // buffered data is processed or the crypto stream fails to consume data. Any
+  // unconsumed data will be buffered. If the frame is not the next in line, it
+  // will be buffered.
+  void OnCryptoFrame(const QuicCryptoFrame& frame);
+
   // Once data is buffered, it's up to the stream to read it when the stream
   // can handle more data.  The following three functions make that possible.
 
@@ -162,6 +169,11 @@
   // the stream of FIN, and clear buffers.
   bool MaybeCloseStream();
 
+  // Shared implementation between OnStreamFrame and OnCryptoFrame.
+  void OnFrameData(QuicStreamOffset byte_offset,
+                   size_t data_len,
+                   const char* data_buffer);
+
   // The stream which owns this sequencer.
   StreamInterface* stream_;
 
diff --git a/quic/core/quic_stream_sequencer_buffer.cc b/quic/core/quic_stream_sequencer_buffer.cc
index b96a89a..2f09095 100644
--- a/quic/core/quic_stream_sequencer_buffer.cc
+++ b/quic/core/quic_stream_sequencer_buffer.cc
@@ -4,10 +4,11 @@
 
 #include "net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h"
 
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
-#include "net/third_party/quiche/src/quic/platform/api/quic_interval.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
diff --git a/quic/core/quic_stream_sequencer_buffer.h b/quic/core/quic_stream_sequencer_buffer.h
index 900fad0..09646b9 100644
--- a/quic/core/quic_stream_sequencer_buffer.h
+++ b/quic/core/quic_stream_sequencer_buffer.h
@@ -64,6 +64,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval_set.h"
 #include "net/third_party/quiche/src/quic/core/quic_packets.h"
 #include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
diff --git a/quic/core/quic_stream_test.cc b/quic/core/quic_stream_test.cc
index e8338b3..d019f71 100644
--- a/quic/core/quic_stream_test.cc
+++ b/quic/core/quic_stream_test.cc
@@ -61,7 +61,6 @@
   using QuicStream::CloseWriteSide;
   using QuicStream::fin_buffered;
   using QuicStream::OnClose;
-  using QuicStream::set_ack_listener;
   using QuicStream::WriteMemSlices;
   using QuicStream::WriteOrBufferData;
   using QuicStream::WritevData;
@@ -160,17 +159,16 @@
 
 // Index value of 1 has the test run with supported-version[1], which is some
 // version OTHER than 99.
-INSTANTIATE_TEST_CASE_P(
-    QuicStreamTests,
-    QuicStreamTest,
+INSTANTIATE_TEST_SUITE_P(
+    QuicStreamTests, QuicStreamTest,
     ::testing::ValuesIn(ParsedVersionOfIndex(AllSupportedVersions(), 1)));
 
 // Make a parameterized version of the QuicStreamTest for those tests
 // that need to differentiate based on version number.
 class QuicParameterizedStreamTest : public QuicStreamTestBase {};
-INSTANTIATE_TEST_CASE_P(QuicParameterizedStreamTests,
-                        QuicParameterizedStreamTest,
-                        ::testing::ValuesIn(AllSupportedVersions()));
+INSTANTIATE_TEST_SUITE_P(QuicParameterizedStreamTests,
+                         QuicParameterizedStreamTest,
+                         ::testing::ValuesIn(AllSupportedVersions()));
 
 TEST_P(QuicStreamTest, PendingStreamTooMuchData) {
   Initialize();
@@ -271,7 +269,9 @@
       1 + QuicPacketCreator::StreamFramePacketOverhead(
               connection_->transport_version(), PACKET_8BYTE_CONNECTION_ID,
               PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion,
-              !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER, 0u);
+              !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER,
+              VARIABLE_LENGTH_INTEGER_LENGTH_0,
+              VARIABLE_LENGTH_INTEGER_LENGTH_0, 0u);
   connection_->SetMaxPacketLength(length);
 
   EXPECT_CALL(*session_, WritevData(stream_, kTestStreamId, _, _, _))
@@ -352,7 +352,9 @@
       1 + QuicPacketCreator::StreamFramePacketOverhead(
               connection_->transport_version(), PACKET_8BYTE_CONNECTION_ID,
               PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion,
-              !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER, 0u);
+              !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER,
+              VARIABLE_LENGTH_INTEGER_LENGTH_0,
+              VARIABLE_LENGTH_INTEGER_LENGTH_0, 0u);
   connection_->SetMaxPacketLength(length);
 
   EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
@@ -788,9 +790,6 @@
 
 TEST_P(QuicStreamTest, StreamWaitsForAcks) {
   Initialize();
-  QuicReferenceCountedPointer<MockAckListener> mock_ack_listener(
-      new StrictMock<MockAckListener>);
-  stream_->set_ack_listener(mock_ack_listener);
   EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
       .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
   // Stream is not waiting for acks initially.
@@ -801,9 +800,10 @@
   stream_->WriteOrBufferData(kData1, false, nullptr);
   EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
   EXPECT_TRUE(stream_->IsWaitingForAcks());
-  EXPECT_CALL(*mock_ack_listener, OnPacketAcked(9, _));
-  EXPECT_TRUE(
-      stream_->OnStreamFrameAcked(0, 9, false, QuicTime::Delta::Zero()));
+  QuicByteCount newly_acked_length = 0;
+  EXPECT_TRUE(stream_->OnStreamFrameAcked(0, 9, false, QuicTime::Delta::Zero(),
+                                          &newly_acked_length));
+  EXPECT_EQ(9u, newly_acked_length);
   // Stream is not waiting for acks as all sent data is acked.
   EXPECT_FALSE(stream_->IsWaitingForAcks());
   EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
@@ -818,21 +818,20 @@
   EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
 
   // kData2 is retransmitted.
-  EXPECT_CALL(*mock_ack_listener, OnPacketRetransmitted(9));
   stream_->OnStreamFrameRetransmitted(9, 9, false);
 
   // kData2 is acked.
-  EXPECT_CALL(*mock_ack_listener, OnPacketAcked(9, _));
-  EXPECT_TRUE(
-      stream_->OnStreamFrameAcked(9, 9, false, QuicTime::Delta::Zero()));
+  EXPECT_TRUE(stream_->OnStreamFrameAcked(9, 9, false, QuicTime::Delta::Zero(),
+                                          &newly_acked_length));
+  EXPECT_EQ(9u, newly_acked_length);
   // Stream is waiting for acks as FIN is not acked.
   EXPECT_TRUE(stream_->IsWaitingForAcks());
   EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
 
   // FIN is acked.
-  EXPECT_CALL(*mock_ack_listener, OnPacketAcked(0, _));
-  EXPECT_TRUE(
-      stream_->OnStreamFrameAcked(18, 0, true, QuicTime::Delta::Zero()));
+  EXPECT_TRUE(stream_->OnStreamFrameAcked(18, 0, true, QuicTime::Delta::Zero(),
+                                          &newly_acked_length));
+  EXPECT_EQ(0u, newly_acked_length);
   EXPECT_FALSE(stream_->IsWaitingForAcks());
   EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
 }
@@ -849,19 +848,24 @@
   EXPECT_EQ(3u, QuicStreamPeer::SendBuffer(stream_).size());
   EXPECT_TRUE(stream_->IsWaitingForAcks());
 
-  EXPECT_TRUE(
-      stream_->OnStreamFrameAcked(9, 9, false, QuicTime::Delta::Zero()));
+  QuicByteCount newly_acked_length = 0;
+  EXPECT_TRUE(stream_->OnStreamFrameAcked(9, 9, false, QuicTime::Delta::Zero(),
+                                          &newly_acked_length));
+  EXPECT_EQ(9u, newly_acked_length);
   EXPECT_EQ(3u, QuicStreamPeer::SendBuffer(stream_).size());
-  EXPECT_TRUE(
-      stream_->OnStreamFrameAcked(18, 9, false, QuicTime::Delta::Zero()));
+  EXPECT_TRUE(stream_->OnStreamFrameAcked(18, 9, false, QuicTime::Delta::Zero(),
+                                          &newly_acked_length));
+  EXPECT_EQ(9u, newly_acked_length);
   EXPECT_EQ(3u, QuicStreamPeer::SendBuffer(stream_).size());
-  EXPECT_TRUE(
-      stream_->OnStreamFrameAcked(0, 9, false, QuicTime::Delta::Zero()));
+  EXPECT_TRUE(stream_->OnStreamFrameAcked(0, 9, false, QuicTime::Delta::Zero(),
+                                          &newly_acked_length));
+  EXPECT_EQ(9u, newly_acked_length);
   EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
   // FIN is not acked yet.
   EXPECT_TRUE(stream_->IsWaitingForAcks());
-  EXPECT_TRUE(
-      stream_->OnStreamFrameAcked(27, 0, true, QuicTime::Delta::Zero()));
+  EXPECT_TRUE(stream_->OnStreamFrameAcked(27, 0, true, QuicTime::Delta::Zero(),
+                                          &newly_acked_length));
+  EXPECT_EQ(0u, newly_acked_length);
   EXPECT_FALSE(stream_->IsWaitingForAcks());
 }
 
@@ -1183,9 +1187,6 @@
 
 TEST_P(QuicStreamTest, StreamDataGetAckedMultipleTimes) {
   Initialize();
-  QuicReferenceCountedPointer<MockAckListener> mock_ack_listener(
-      new StrictMock<MockAckListener>);
-  stream_->set_ack_listener(mock_ack_listener);
   EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
       .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
   // Send [0, 27) and fin.
@@ -1197,42 +1198,41 @@
 
   // Ack [0, 9), [5, 22) and [18, 26)
   // Verify [0, 9) 9 bytes are acked.
-  EXPECT_CALL(*mock_ack_listener, OnPacketAcked(9, _));
-  EXPECT_TRUE(
-      stream_->OnStreamFrameAcked(0, 9, false, QuicTime::Delta::Zero()));
+  QuicByteCount newly_acked_length = 0;
+  EXPECT_TRUE(stream_->OnStreamFrameAcked(0, 9, false, QuicTime::Delta::Zero(),
+                                          &newly_acked_length));
+  EXPECT_EQ(9u, newly_acked_length);
   EXPECT_EQ(2u, QuicStreamPeer::SendBuffer(stream_).size());
   // Verify [9, 22) 13 bytes are acked.
-  EXPECT_CALL(*mock_ack_listener, OnPacketAcked(13, _));
-  EXPECT_TRUE(
-      stream_->OnStreamFrameAcked(5, 17, false, QuicTime::Delta::Zero()));
+  EXPECT_TRUE(stream_->OnStreamFrameAcked(5, 17, false, QuicTime::Delta::Zero(),
+                                          &newly_acked_length));
+  EXPECT_EQ(13u, newly_acked_length);
   EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
   // Verify [22, 26) 4 bytes are acked.
-  EXPECT_CALL(*mock_ack_listener, OnPacketAcked(4, _));
-  EXPECT_TRUE(
-      stream_->OnStreamFrameAcked(18, 8, false, QuicTime::Delta::Zero()));
+  EXPECT_TRUE(stream_->OnStreamFrameAcked(18, 8, false, QuicTime::Delta::Zero(),
+                                          &newly_acked_length));
+  EXPECT_EQ(4u, newly_acked_length);
   EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
   EXPECT_TRUE(stream_->IsWaitingForAcks());
 
-  // Ack [0, 27).
-  // Verify [26, 27) 1 byte is acked.
-  EXPECT_CALL(*mock_ack_listener, OnPacketAcked(1, _));
-  EXPECT_TRUE(
-      stream_->OnStreamFrameAcked(26, 1, false, QuicTime::Delta::Zero()));
+  // Ack [0, 27). Verify [26, 27) 1 byte is acked.
+  EXPECT_TRUE(stream_->OnStreamFrameAcked(26, 1, false, QuicTime::Delta::Zero(),
+                                          &newly_acked_length));
+  EXPECT_EQ(1u, newly_acked_length);
   EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
   EXPECT_TRUE(stream_->IsWaitingForAcks());
 
-  // Ack Fin. Verify OnPacketAcked is called.
-  EXPECT_CALL(*mock_ack_listener, OnPacketAcked(0, _));
-  EXPECT_TRUE(
-      stream_->OnStreamFrameAcked(27, 0, true, QuicTime::Delta::Zero()));
+  // Ack Fin.
+  EXPECT_TRUE(stream_->OnStreamFrameAcked(27, 0, true, QuicTime::Delta::Zero(),
+                                          &newly_acked_length));
+  EXPECT_EQ(0u, newly_acked_length);
   EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
   EXPECT_FALSE(stream_->IsWaitingForAcks());
 
-  // Ack [10, 27) and fin.
-  // No new data is acked, verify OnPacketAcked is not called.
-  EXPECT_CALL(*mock_ack_listener, OnPacketAcked(_, _)).Times(0);
-  EXPECT_FALSE(
-      stream_->OnStreamFrameAcked(10, 17, true, QuicTime::Delta::Zero()));
+  // Ack [10, 27) and fin. No new data is acked.
+  EXPECT_FALSE(stream_->OnStreamFrameAcked(
+      10, 17, true, QuicTime::Delta::Zero(), &newly_acked_length));
+  EXPECT_EQ(0u, newly_acked_length);
   EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
   EXPECT_FALSE(stream_->IsWaitingForAcks());
 }
@@ -1282,8 +1282,10 @@
   EXPECT_TRUE(stream_->HasPendingRetransmission());
 
   // Ack [9, 18).
-  EXPECT_TRUE(
-      stream_->OnStreamFrameAcked(9, 9, false, QuicTime::Delta::Zero()));
+  QuicByteCount newly_acked_length = 0;
+  EXPECT_TRUE(stream_->OnStreamFrameAcked(9, 9, false, QuicTime::Delta::Zero(),
+                                          &newly_acked_length));
+  EXPECT_EQ(9u, newly_acked_length);
   EXPECT_FALSE(stream_->IsStreamFrameOutstanding(9, 3, false));
   EXPECT_TRUE(stream_->HasPendingRetransmission());
   // This OnCanWrite causes [18, 27) and fin to be retransmitted. Verify fin can
@@ -1385,8 +1387,10 @@
   stream_->WriteOrBufferData(kData1, false, nullptr);
   stream_->WriteOrBufferData(kData1, true, nullptr);
   // Ack [10, 13).
-  stream_->OnStreamFrameAcked(10, 3, false, QuicTime::Delta::Zero());
-
+  QuicByteCount newly_acked_length = 0;
+  stream_->OnStreamFrameAcked(10, 3, false, QuicTime::Delta::Zero(),
+                              &newly_acked_length);
+  EXPECT_EQ(3u, newly_acked_length);
   // Retransmit [0, 18) with fin, and only [0, 8) is consumed.
   EXPECT_CALL(*session_, WritevData(_, stream_->id(), 10, 0, NO_FIN))
       .WillOnce(InvokeWithoutArgs([this]() {
diff --git a/quic/core/quic_time_wait_list_manager.cc b/quic/core/quic_time_wait_list_manager.cc
index 731df3e..5335455 100644
--- a/quic/core/quic_time_wait_list_manager.cc
+++ b/quic/core/quic_time_wait_list_manager.cc
@@ -336,9 +336,6 @@
 
 QuicUint128 QuicTimeWaitListManager::GetStatelessResetToken(
     QuicConnectionId connection_id) const {
-  if (!QuicConnectionIdSupportsVariableLength(Perspective::IS_SERVER)) {
-    return QuicConnectionIdToUInt64(connection_id);
-  }
   return QuicUtils::GenerateStatelessResetToken(connection_id);
 }
 
diff --git a/quic/core/quic_time_wait_list_manager_test.cc b/quic/core/quic_time_wait_list_manager_test.cc
index 0d3244b..263702a 100644
--- a/quic/core/quic_time_wait_list_manager_test.cc
+++ b/quic/core/quic_time_wait_list_manager_test.cc
@@ -55,9 +55,6 @@
   }
 
   bool IsValidStatelessResetToken(QuicUint128 token) const override {
-    if (!QuicConnectionIdSupportsVariableLength(Perspective::IS_SERVER)) {
-      return token == QuicConnectionIdToUInt64(connection_id_);
-    }
     return token == QuicUtils::GenerateStatelessResetToken(connection_id_);
   }
 
@@ -144,8 +141,6 @@
   void SetUp() override {
     EXPECT_CALL(writer_, IsWriteBlocked())
         .WillRepeatedly(ReturnPointee(&writer_is_blocked_));
-    EXPECT_CALL(writer_, IsWriteBlockedDataBuffered())
-        .WillRepeatedly(Return(false));
   }
 
   void AddConnectionId(QuicConnectionId connection_id,
@@ -221,14 +216,8 @@
   QuicIetfStatelessResetPacket stateless_reset =
       visitor.stateless_reset_packet();
 
-  QuicUint128 expected_stateless_reset_token;
-  if (!QuicConnectionIdSupportsVariableLength(Perspective::IS_SERVER)) {
-    expected_stateless_reset_token =
-        QuicConnectionIdToUInt64(expected_connection_id);
-  } else {
-    expected_stateless_reset_token =
-        QuicUtils::GenerateStatelessResetToken(expected_connection_id);
-  }
+  QuicUint128 expected_stateless_reset_token =
+      QuicUtils::GenerateStatelessResetToken(expected_connection_id);
 
   bool stateless_reset_is_valid =
       stateless_reset.stateless_reset_token == expected_stateless_reset_token;
diff --git a/quic/core/quic_trace_visitor.cc b/quic/core/quic_trace_visitor.cc
index b0fc8ae..ba2ed7f 100644
--- a/quic/core/quic_trace_visitor.cc
+++ b/quic/core/quic_trace_visitor.cc
@@ -13,7 +13,7 @@
   switch (level) {
     case ENCRYPTION_NONE:
       return quic_trace::ENCRYPTION_INITIAL;
-    case ENCRYPTION_INITIAL:
+    case ENCRYPTION_ZERO_RTT:
       return quic_trace::ENCRYPTION_0RTT;
     case ENCRYPTION_FORWARD_SECURE:
       return quic_trace::ENCRYPTION_1RTT;
@@ -26,20 +26,8 @@
 QuicTraceVisitor::QuicTraceVisitor(const QuicConnection* connection)
     : connection_(connection),
       start_time_(connection_->clock()->ApproximateNow()) {
-  QuicString binary_connection_id;
-  if (!QuicConnectionIdSupportsVariableLength(connection->perspective())) {
-    // QUIC CIDs are currently represented in memory as a converted
-    // representation of the on-wire ID.  Convert it back to wire format before
-    // recording, since the standard treats it as an opaque blob.
-    uint64_t connection_id = QuicEndian::HostToNet64(
-        QuicConnectionIdToUInt64(connection->connection_id()));
-    binary_connection_id = QuicString(
-        reinterpret_cast<const char*>(&connection_id), sizeof(connection_id));
-  } else {
-    binary_connection_id.assign(connection->connection_id().data(),
-                                connection->connection_id().length());
-  }
-
+  QuicString binary_connection_id(connection->connection_id().data(),
+                                  connection->connection_id().length());
   // We assume that the connection ID in gQUIC is equivalent to the
   // server-chosen client-selected ID.
   switch (connection->perspective()) {
@@ -338,4 +326,4 @@
   }
 }
 
-};  // namespace quic
+}  // namespace quic
diff --git a/quic/core/quic_trace_visitor.h b/quic/core/quic_trace_visitor.h
index efbbee3..a3e7dda 100644
--- a/quic/core/quic_trace_visitor.h
+++ b/quic/core/quic_trace_visitor.h
@@ -65,6 +65,6 @@
   const QuicTime start_time_;
 };
 
-};  // namespace quic
+}  // namespace quic
 
 #endif  // QUICHE_QUIC_CORE_QUIC_TRACE_VISITOR_H_
diff --git a/quic/core/quic_types.h b/quic/core/quic_types.h
index 7718468..bf7943d 100644
--- a/quic/core/quic_types.h
+++ b/quic/core/quic_types.h
@@ -177,6 +177,7 @@
   BLOCKED_FRAME = 5,
   STOP_WAITING_FRAME = 6,
   PING_FRAME = 7,
+  CRYPTO_FRAME = 8,
 
   // STREAM and ACK frames are special frames. They are encoded differently on
   // the wire and their values do not need to be stable.
@@ -197,7 +198,6 @@
   PATH_CHALLENGE_FRAME,
   STOP_SENDING_FRAME,
   MESSAGE_FRAME,
-  CRYPTO_FRAME,
   NEW_TOKEN_FRAME,
   RETIRE_CONNECTION_ID_FRAME,
 
@@ -234,15 +234,14 @@
   // 0x09 through 0x0f are various flag settings of the IETF_STREAM frame.
   IETF_MAX_DATA = 0x10,
   IETF_MAX_STREAM_DATA = 0x11,
-  IETF_MAX_STREAM_ID = 0x12,  // TODO(fkastenholz): Will become IETF_MAX_STREAMS
-  // 0x13 reserved, a flag setting for IETF_MAX_STREAMS.
+  IETF_MAX_STREAMS_BIDIRECTIONAL = 0x12,
+  IETF_MAX_STREAMS_UNIDIRECTIONAL = 0x13,
   IETF_BLOCKED = 0x14,  // TODO(fkastenholz): Should, eventually, be renamed to
                         // IETF_DATA_BLOCKED
   IETF_STREAM_BLOCKED = 0x15,  // TODO(fkastenholz): Should, eventually, be
                                // renamed to IETF_STREAM_DATA_BLOCKED
-  IETF_STREAM_ID_BLOCKED =
-      0x16,  // TODO(fkastenholz): Will become IETF_STREAMS_BLOCKED
-  // 0x17 reserved, a flag setting for IETF_STREAMS_BLOCKED
+  IETF_STREAMS_BLOCKED_BIDIRECTIONAL = 0x16,
+  IETF_STREAMS_BLOCKED_UNIDIRECTIONAL = 0x17,
   IETF_NEW_CONNECTION_ID = 0x18,
   IETF_RETIRE_CONNECTION_ID = 0x19,
   IETF_PATH_CHALLENGE = 0x1a,
@@ -271,10 +270,25 @@
 #define IETF_STREAM_FRAME_LEN_BIT 0x02
 #define IETF_STREAM_FRAME_OFF_BIT 0x04
 
+enum QuicVariableLengthIntegerLength : uint8_t {
+  // Length zero means the variable length integer is not present.
+  VARIABLE_LENGTH_INTEGER_LENGTH_0 = 0,
+  VARIABLE_LENGTH_INTEGER_LENGTH_1 = 1,
+  VARIABLE_LENGTH_INTEGER_LENGTH_2 = 2,
+  VARIABLE_LENGTH_INTEGER_LENGTH_4 = 4,
+  VARIABLE_LENGTH_INTEGER_LENGTH_8 = 8,
+};
+
+// By default we write the IETF long header length using the 2-byte encoding
+// of variable length integers, even when the length is below 64, which allows
+// us to fill in the length before knowing what the length actually is.
+const QuicVariableLengthIntegerLength kQuicDefaultLongHeaderLengthLength =
+    VARIABLE_LENGTH_INTEGER_LENGTH_2;
+
 enum QuicPacketNumberLength : uint8_t {
   PACKET_1BYTE_PACKET_NUMBER = 1,
   PACKET_2BYTE_PACKET_NUMBER = 2,
-  PACKET_3BYTE_PACKET_NUMBER = 3,  // Used in version > QUIC_VERSION_46.
+  PACKET_3BYTE_PACKET_NUMBER = 3,  // Used in version > QUIC_VERSION_44.
   PACKET_4BYTE_PACKET_NUMBER = 4,
   // TODO(rch): Remove this when we remove QUIC_VERSION_39.
   PACKET_6BYTE_PACKET_NUMBER = 6,
@@ -359,7 +373,7 @@
 // understand.
 enum EncryptionLevel : int8_t {
   ENCRYPTION_NONE = 0,
-  ENCRYPTION_INITIAL = 1,
+  ENCRYPTION_ZERO_RTT = 1,
   ENCRYPTION_FORWARD_SECURE = 2,
 
   NUM_ENCRYPTION_LEVELS,
@@ -551,6 +565,16 @@
   READ_UNIDIRECTIONAL,
 };
 
+// A packet number space is the context in which a packet can be processed and
+// acknowledged.
+enum PacketNumberSpace : uint8_t {
+  INITIAL_DATA = 0,  // Only used in IETF QUIC.
+  HANDSHAKE_DATA = 1,
+  APPLICATION_DATA = 2,
+
+  NUM_PACKET_NUMBER_SPACES,
+};
+
 }  // namespace quic
 
 #endif  // QUICHE_QUIC_CORE_QUIC_TYPES_H_
diff --git a/quic/core/quic_unacked_packet_map.cc b/quic/core/quic_unacked_packet_map.cc
index 32fd272..859bf39 100644
--- a/quic/core/quic_unacked_packet_map.cc
+++ b/quic/core/quic_unacked_packet_map.cc
@@ -24,13 +24,20 @@
 }
 }  // namespace
 
-QuicUnackedPacketMap::QuicUnackedPacketMap()
-    : least_unacked_(FirstSendingPacketNumber()),
+QuicUnackedPacketMap::QuicUnackedPacketMap(Perspective perspective)
+    : perspective_(perspective),
+      least_unacked_(FirstSendingPacketNumber()),
       bytes_in_flight_(0),
       pending_crypto_packet_count_(0),
       last_crypto_packet_sent_time_(QuicTime::Zero()),
       session_notifier_(nullptr),
-      session_decides_what_to_write_(false) {}
+      session_decides_what_to_write_(false),
+      use_uber_loss_algorithm_(
+          GetQuicReloadableFlag(quic_use_uber_loss_algorithm)) {
+  if (use_uber_loss_algorithm_) {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_use_uber_loss_algorithm);
+  }
+}
 
 QuicUnackedPacketMap::~QuicUnackedPacketMap() {
   for (QuicTransmissionInfo& transmission_info : unacked_packets_) {
@@ -76,7 +83,12 @@
   if (set_in_flight) {
     bytes_in_flight_ += bytes_sent;
     info.in_flight = true;
-    largest_sent_retransmittable_packet_ = packet_number;
+    if (use_uber_loss_algorithm_) {
+      largest_sent_retransmittable_packets_[GetPacketNumberSpace(
+          info.encryption_level)] = packet_number;
+    } else {
+      largest_sent_retransmittable_packet_ = packet_number;
+    }
   }
   unacked_packets_.push_back(info);
   // Swap the retransmittable frames to avoid allocations.
@@ -216,6 +228,20 @@
   largest_acked_ = largest_acked;
 }
 
+void QuicUnackedPacketMap::MaybeUpdateLargestAckedOfPacketNumberSpace(
+    EncryptionLevel encryption_level,
+    QuicPacketNumber packet_number) {
+  DCHECK(use_uber_loss_algorithm_);
+  const PacketNumberSpace packet_number_space =
+      GetPacketNumberSpace(encryption_level);
+  if (!largest_acked_packets_[packet_number_space].IsInitialized()) {
+    largest_acked_packets_[packet_number_space] = packet_number;
+  } else {
+    largest_acked_packets_[packet_number_space] =
+        std::max(largest_acked_packets_[packet_number_space], packet_number);
+  }
+}
+
 bool QuicUnackedPacketMap::IsPacketUsefulForMeasuringRtt(
     QuicPacketNumber packet_number,
     const QuicTransmissionInfo& info) const {
@@ -475,6 +501,45 @@
   aggregated_stream_frame_.stream_id = -1;
 }
 
+PacketNumberSpace QuicUnackedPacketMap::GetPacketNumberSpace(
+    QuicPacketNumber packet_number) const {
+  DCHECK(use_uber_loss_algorithm_);
+  return GetPacketNumberSpace(
+      GetTransmissionInfo(packet_number).encryption_level);
+}
+
+PacketNumberSpace QuicUnackedPacketMap::GetPacketNumberSpace(
+    EncryptionLevel encryption_level) const {
+  DCHECK(use_uber_loss_algorithm_);
+  if (perspective_ == Perspective::IS_CLIENT) {
+    return encryption_level == ENCRYPTION_NONE ? HANDSHAKE_DATA
+                                               : APPLICATION_DATA;
+  }
+  return encryption_level == ENCRYPTION_FORWARD_SECURE ? APPLICATION_DATA
+                                                       : HANDSHAKE_DATA;
+}
+
+QuicPacketNumber QuicUnackedPacketMap::GetLargestAckedOfPacketNumberSpace(
+    PacketNumberSpace packet_number_space) const {
+  DCHECK(use_uber_loss_algorithm_);
+  if (packet_number_space >= NUM_PACKET_NUMBER_SPACES) {
+    QUIC_BUG << "Invalid packet number space: " << packet_number_space;
+    return QuicPacketNumber();
+  }
+  return largest_acked_packets_[packet_number_space];
+}
+
+QuicPacketNumber
+QuicUnackedPacketMap::GetLargestSentRetransmittableOfPacketNumberSpace(
+    PacketNumberSpace packet_number_space) const {
+  DCHECK(use_uber_loss_algorithm_);
+  if (packet_number_space >= NUM_PACKET_NUMBER_SPACES) {
+    QUIC_BUG << "Invalid packet number space: " << packet_number_space;
+    return QuicPacketNumber();
+  }
+  return largest_sent_retransmittable_packets_[packet_number_space];
+}
+
 void QuicUnackedPacketMap::SetSessionDecideWhatToWrite(
     bool session_decides_what_to_write) {
   if (largest_sent_packet_.IsInitialized()) {
diff --git a/quic/core/quic_unacked_packet_map.h b/quic/core/quic_unacked_packet_map.h
index b4d13c5..b6f7af5 100644
--- a/quic/core/quic_unacked_packet_map.h
+++ b/quic/core/quic_unacked_packet_map.h
@@ -26,7 +26,7 @@
 // 3) Track sent time of packets to provide RTT measurements from acks.
 class QUIC_EXPORT_PRIVATE QuicUnackedPacketMap {
  public:
-  QuicUnackedPacketMap();
+  QuicUnackedPacketMap(Perspective perspective);
   QuicUnackedPacketMap(const QuicUnackedPacketMap&) = delete;
   QuicUnackedPacketMap& operator=(const QuicUnackedPacketMap&) = delete;
   ~QuicUnackedPacketMap();
@@ -93,6 +93,7 @@
 
   // Returns the largest retransmittable packet number that has been sent.
   QuicPacketNumber largest_sent_retransmittable_packet() const {
+    DCHECK(!use_uber_loss_algorithm_);
     return largest_sent_retransmittable_packet_;
   }
 
@@ -165,6 +166,12 @@
   // |largest_acked| are discarded if they are only for the RTT purposes.
   void IncreaseLargestAcked(QuicPacketNumber largest_acked);
 
+  // Called when |packet_number| gets acked. Maybe increase the largest acked of
+  // corresponding packet number space of |encryption_level|.
+  void MaybeUpdateLargestAckedOfPacketNumberSpace(
+      EncryptionLevel encryption_level,
+      QuicPacketNumber packet_number);
+
   // Remove any packets no longer needed for retransmission, congestion, or
   // RTT measurement purposes.
   void RemoveObsoletePackets();
@@ -180,6 +187,24 @@
   // stream id.
   void NotifyAggregatedStreamFrameAcked(QuicTime::Delta ack_delay);
 
+  // Returns packet number space that |packet_number| belongs to. Please use
+  // GetPacketNumberSpace(EncryptionLevel) whenever encryption level is
+  // available.
+  PacketNumberSpace GetPacketNumberSpace(QuicPacketNumber packet_number) const;
+
+  // Returns packet number space of |encryption_level|.
+  PacketNumberSpace GetPacketNumberSpace(
+      EncryptionLevel encryption_level) const;
+
+  // Returns largest acked packet number of |packet_number_space|.
+  QuicPacketNumber GetLargestAckedOfPacketNumberSpace(
+      PacketNumberSpace packet_number_space) const;
+
+  // Returns largest sent retransmittable packet number of
+  // |packet_number_space|.
+  QuicPacketNumber GetLargestSentRetransmittableOfPacketNumberSpace(
+      PacketNumberSpace packet_number_space) const;
+
   // Called to start/stop letting session decide what to write.
   void SetSessionDecideWhatToWrite(bool session_decides_what_to_write);
 
@@ -189,6 +214,10 @@
     return session_decides_what_to_write_;
   }
 
+  bool use_uber_loss_algorithm() const { return use_uber_loss_algorithm_; }
+
+  Perspective perspective() const { return perspective_; }
+
  private:
   friend class test::QuicUnackedPacketMapPeer;
 
@@ -218,13 +247,24 @@
   bool IsPacketUseless(QuicPacketNumber packet_number,
                        const QuicTransmissionInfo& info) const;
 
+  const Perspective perspective_;
+
   QuicPacketNumber largest_sent_packet_;
   // The largest sent packet we expect to receive an ack for.
+  // TODO(fayang): Remove largest_sent_retransmittable_packet_ when deprecating
+  // quic_use_uber_loss_algorithm.
   QuicPacketNumber largest_sent_retransmittable_packet_;
+  // The largest sent packet we expect to receive an ack for per packet number
+  // space. Only used if use_uber_loss_algorithm_ is true.
+  QuicPacketNumber
+      largest_sent_retransmittable_packets_[NUM_PACKET_NUMBER_SPACES];
   // The largest sent largest_acked in an ACK frame.
   QuicPacketNumber largest_sent_largest_acked_;
   // The largest received largest_acked from an ACK frame.
   QuicPacketNumber largest_acked_;
+  // The largest received largest_acked from ACK frame per packet number space.
+  // Only used if use_uber_loss_algorithm_ is true.
+  QuicPacketNumber largest_acked_packets_[NUM_PACKET_NUMBER_SPACES];
 
   // Newly serialized retransmittable packets are added to this map, which
   // contains owning pointers to any contained frames.  If a packet is
@@ -254,6 +294,9 @@
 
   // If true, let session decides what to write.
   bool session_decides_what_to_write_;
+
+  // Latched value of quic_use_uber_loss_algorithm.
+  const bool use_uber_loss_algorithm_;
 };
 
 }  // namespace quic
diff --git a/quic/core/quic_unacked_packet_map_test.cc b/quic/core/quic_unacked_packet_map_test.cc
index df14321..cf725fa 100644
--- a/quic/core/quic_unacked_packet_map_test.cc
+++ b/quic/core/quic_unacked_packet_map_test.cc
@@ -11,6 +11,7 @@
 #include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_unacked_packet_map_peer.h"
 
 using testing::_;
 using testing::Return;
@@ -18,27 +19,46 @@
 
 namespace quic {
 namespace test {
-
-class QuicUnackedPacketMapPeer {
- public:
-  static const QuicStreamFrame& GetAggregatedStreamFrame(
-      const QuicUnackedPacketMap& unacked_packets) {
-    return unacked_packets.aggregated_stream_frame_;
-  }
-};
-
 namespace {
 
 // Default packet length.
 const uint32_t kDefaultLength = 1000;
 
-class QuicUnackedPacketMapTest : public QuicTestWithParam<bool> {
+struct TestParams {
+  TestParams(Perspective perspective, bool session_decides_what_to_write)
+      : perspective(perspective),
+        session_decides_what_to_write(session_decides_what_to_write) {}
+
+  friend std::ostream& operator<<(std::ostream& os, const TestParams& p) {
+    os << "{ Perspective: " << p.perspective
+       << " session_decides_what_to_write: " << p.session_decides_what_to_write
+       << " }";
+    return os;
+  }
+
+  Perspective perspective;
+  bool session_decides_what_to_write;
+};
+
+std::vector<TestParams> GetTestParams() {
+  std::vector<TestParams> params;
+  for (Perspective perspective :
+       {Perspective::IS_CLIENT, Perspective::IS_SERVER}) {
+    for (bool session_decides_what_to_write : {true, false}) {
+      params.push_back(TestParams(perspective, session_decides_what_to_write));
+    }
+  }
+  return params;
+}
+
+class QuicUnackedPacketMapTest : public QuicTestWithParam<TestParams> {
  protected:
   QuicUnackedPacketMapTest()
-      : unacked_packets_(),
+      : unacked_packets_(GetParam().perspective),
         now_(QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1000)) {
     unacked_packets_.SetSessionNotifier(&notifier_);
-    unacked_packets_.SetSessionDecideWhatToWrite(GetParam());
+    unacked_packets_.SetSessionDecideWhatToWrite(
+        GetParam().session_decides_what_to_write);
     EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(true));
     EXPECT_CALL(notifier_, OnStreamFrameRetransmitted(_))
         .Times(testing::AnyNumber());
@@ -178,7 +198,9 @@
   StrictMock<MockSessionNotifier> notifier_;
 };
 
-INSTANTIATE_TEST_CASE_P(Tests, QuicUnackedPacketMapTest, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Tests,
+                         QuicUnackedPacketMapTest,
+                         ::testing::ValuesIn(GetTestParams()));
 
 TEST_P(QuicUnackedPacketMapTest, RttOnly) {
   // Acks are only tracked for RTT measurement purposes.
diff --git a/quic/core/quic_utils.cc b/quic/core/quic_utils.cc
index be6eeea..0801f41 100644
--- a/quic/core/quic_utils.cc
+++ b/quic/core/quic_utils.cc
@@ -143,7 +143,7 @@
 const char* QuicUtils::EncryptionLevelToString(EncryptionLevel level) {
   switch (level) {
     RETURN_STRING_LITERAL(ENCRYPTION_NONE);
-    RETURN_STRING_LITERAL(ENCRYPTION_INITIAL);
+    RETURN_STRING_LITERAL(ENCRYPTION_ZERO_RTT);
     RETURN_STRING_LITERAL(ENCRYPTION_FORWARD_SECURE);
     RETURN_STRING_LITERAL(NUM_ENCRYPTION_LEVELS);
   }
@@ -290,6 +290,13 @@
 }
 
 // static
+struct iovec QuicUtils::MakeIovec(QuicStringPiece data) {
+  struct iovec iov = {const_cast<char*>(data.data()),
+                      static_cast<size_t>(data.size())};
+  return iov;
+}
+
+// static
 bool QuicUtils::IsAckable(SentPacketState state) {
   return state != NEVER_SENT && state != ACKED && state != UNACKABLE;
 }
@@ -308,6 +315,17 @@
 }
 
 // static
+bool QuicUtils::IsHandshakeFrame(const QuicFrame& frame,
+                                 QuicTransportVersion transport_version) {
+  if (transport_version < QUIC_VERSION_47) {
+    return frame.type == STREAM_FRAME &&
+           frame.stream_frame.stream_id == GetCryptoStreamId(transport_version);
+  } else {
+    return frame.type == CRYPTO_FRAME;
+  }
+}
+
+// static
 SentPacketState QuicUtils::RetransmissionTypeToPacketState(
     TransmissionType retransmission_type) {
   switch (retransmission_type) {
@@ -347,11 +365,14 @@
 
 // static
 QuicStreamId QuicUtils::GetInvalidStreamId(QuicTransportVersion version) {
-  return version == QUIC_VERSION_99 ? -1 : 0;
+  return version == QUIC_VERSION_99 ? std::numeric_limits<QuicStreamId>::max()
+                                    : 0;
 }
 
 // static
 QuicStreamId QuicUtils::GetCryptoStreamId(QuicTransportVersion version) {
+  // TODO(nharper): Change this to return GetInvalidStreamId for version 47 or
+  // greater. Currently, too many things break with that change.
   return version == QUIC_VERSION_99 ? 0 : 1;
 }
 
@@ -384,11 +405,30 @@
 }
 
 // static
-StreamType QuicUtils::GetStreamType(QuicStreamId id, bool peer_initiated) {
+StreamType QuicUtils::GetStreamType(QuicStreamId id,
+                                    Perspective perspective,
+                                    bool peer_initiated) {
   if (IsBidirectionalStreamId(id)) {
     return BIDIRECTIONAL;
   }
-  return peer_initiated ? READ_UNIDIRECTIONAL : WRITE_UNIDIRECTIONAL;
+
+  if (peer_initiated) {
+    if (perspective == Perspective::IS_SERVER) {
+      DCHECK_EQ(2, id % 4);
+    } else {
+      DCHECK_EQ(Perspective::IS_CLIENT, perspective);
+      DCHECK_EQ(3, id % 4);
+    }
+    return READ_UNIDIRECTIONAL;
+  }
+
+  if (perspective == Perspective::IS_SERVER) {
+    DCHECK_EQ(3, id % 4);
+  } else {
+    DCHECK_EQ(Perspective::IS_CLIENT, perspective);
+    DCHECK_EQ(2, id % 4);
+  }
+  return WRITE_UNIDIRECTIONAL;
 }
 
 // static
@@ -422,21 +462,7 @@
 }
 
 // static
-QuicConnectionId QuicUtils::CreateRandomConnectionId(Perspective perspective) {
-  return CreateRandomConnectionId(QuicRandom::GetInstance(), perspective);
-}
-
-// static
 QuicConnectionId QuicUtils::CreateRandomConnectionId(QuicRandom* random) {
-  return CreateRandomConnectionId(random, Perspective::IS_SERVER);
-}
-
-// static
-QuicConnectionId QuicUtils::CreateRandomConnectionId(QuicRandom* random,
-                                                     Perspective perspective) {
-  if (!QuicConnectionIdSupportsVariableLength(perspective)) {
-    return QuicConnectionIdFromUInt64(random->RandUint64());
-  }
   char connection_id_bytes[kQuicDefaultConnectionIdLength];
   random->RandBytes(connection_id_bytes, QUIC_ARRAYSIZE(connection_id_bytes));
   return QuicConnectionId(static_cast<char*>(connection_id_bytes),
@@ -453,10 +479,6 @@
 // static
 QuicConnectionId QuicUtils::CreateZeroConnectionId(
     QuicTransportVersion version) {
-  if (!QuicConnectionIdSupportsVariableLength(Perspective::IS_SERVER) ||
-      !QuicConnectionIdSupportsVariableLength(Perspective::IS_CLIENT)) {
-    return QuicConnectionIdFromUInt64(0);
-  }
   if (!VariableLengthConnectionIdAllowedForVersion(version)) {
     char connection_id_bytes[8] = {0, 0, 0, 0, 0, 0, 0, 0};
     return QuicConnectionId(static_cast<char*>(connection_id_bytes),
@@ -476,9 +498,6 @@
 
 QuicUint128 QuicUtils::GenerateStatelessResetToken(
     QuicConnectionId connection_id) {
-  if (!QuicConnectionIdUseNetworkByteOrder()) {
-    return QuicConnectionIdToUInt64(connection_id);
-  }
   uint64_t data_bytes[3] = {0, 0, 0};
   static_assert(sizeof(data_bytes) >= kQuicMaxConnectionIdLength,
                 "kQuicMaxConnectionIdLength changed");
diff --git a/quic/core/quic_utils.h b/quic/core/quic_utils.h
index 111080a..28ea15b 100644
--- a/quic/core/quic_utils.h
+++ b/quic/core/quic_utils.h
@@ -10,6 +10,7 @@
 
 #include "base/macros.h"
 #include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_frame.h"
 #include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
 #include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/core/quic_versions.h"
@@ -79,6 +80,9 @@
                            size_t buffer_length,
                            char* buffer);
 
+  // Creates an iovec pointing to the same data as |data|.
+  static struct iovec MakeIovec(QuicStringPiece data);
+
   // Returns true if a packet is ackable. A packet is unackable if it can never
   // be acked. Occurs when a packet is never sent, after it is acknowledged
   // once, or if it's a crypto packet we never expect to receive an ack for.
@@ -88,6 +92,10 @@
   // frame should be retransmitted if it is detected as lost.
   static bool IsRetransmittableFrame(QuicFrameType type);
 
+  // Returns true if |frame| is a handshake frame in version |version|.
+  static bool IsHandshakeFrame(const QuicFrame& frame,
+                               QuicTransportVersion transport_version);
+
   // Returns packet state corresponding to |retransmission_type|.
   static SentPacketState RetransmissionTypeToPacketState(
       TransmissionType retransmission_type);
@@ -121,9 +129,12 @@
   // v99.
   static bool IsBidirectionalStreamId(QuicStreamId id);
 
-  // Returns stream type according to |id| and |peer_initiated|. Only used in
-  // v99.
-  static StreamType GetStreamType(QuicStreamId id, bool peer_initiated);
+  // Returns stream type.  Either |perspective| or |peer_initiated| would be
+  // enough together with |id|.  This method enforces that the three parameters
+  // are consistent.  Only used in v99.
+  static StreamType GetStreamType(QuicStreamId id,
+                                  Perspective perspective,
+                                  bool peer_initiated);
 
   // Returns the delta between consecutive stream IDs of the same type.
   static QuicStreamId StreamIdDelta(QuicTransportVersion version);
@@ -141,16 +152,9 @@
   // Generates a random 64bit connection ID.
   static QuicConnectionId CreateRandomConnectionId();
 
-  // Generates a random 64bit connection ID.
-  static QuicConnectionId CreateRandomConnectionId(Perspective perspective);
-
   // Generates a random 64bit connection ID using the provided QuicRandom.
   static QuicConnectionId CreateRandomConnectionId(QuicRandom* random);
 
-  // Generates a random 64bit connection ID using the provided QuicRandom.
-  static QuicConnectionId CreateRandomConnectionId(QuicRandom* random,
-                                                   Perspective perspective);
-
   // Returns true if the QUIC version allows variable length connection IDs.
   static bool VariableLengthConnectionIdAllowedForVersion(
       QuicTransportVersion version);
diff --git a/quic/core/quic_utils_test.cc b/quic/core/quic_utils_test.cc
index d1b5268..de4c76b 100644
--- a/quic/core/quic_utils_test.cc
+++ b/quic/core/quic_utils_test.cc
@@ -168,16 +168,11 @@
   MockRandom random(33);
   QuicConnectionId connection_id = QuicUtils::CreateRandomConnectionId(&random);
   EXPECT_EQ(connection_id.length(), sizeof(uint64_t));
-  if (!QuicConnectionIdSupportsVariableLength(quic::Perspective::IS_SERVER) ||
-      !QuicConnectionIdSupportsVariableLength(quic::Perspective::IS_CLIENT)) {
-    EXPECT_EQ(connection_id, QuicConnectionIdFromUInt64(random.RandUint64()));
-  } else {
     char connection_id_bytes[sizeof(uint64_t)];
     random.RandBytes(connection_id_bytes, QUIC_ARRAYSIZE(connection_id_bytes));
     EXPECT_EQ(connection_id,
               QuicConnectionId(static_cast<char*>(connection_id_bytes),
                                QUIC_ARRAYSIZE(connection_id_bytes)));
-  }
   EXPECT_NE(connection_id, EmptyQuicConnectionId());
   EXPECT_NE(connection_id, TestConnectionId());
   EXPECT_NE(connection_id, TestConnectionId(1));
@@ -185,19 +180,15 @@
 
 TEST_F(QuicUtilsTest, VariableLengthConnectionId) {
   EXPECT_FALSE(
-      QuicUtils::VariableLengthConnectionIdAllowedForVersion(QUIC_VERSION_35));
+      QuicUtils::VariableLengthConnectionIdAllowedForVersion(QUIC_VERSION_39));
   EXPECT_TRUE(QuicUtils::IsConnectionIdValidForVersion(
-      QuicUtils::CreateZeroConnectionId(QUIC_VERSION_35), QUIC_VERSION_35));
+      QuicUtils::CreateZeroConnectionId(QUIC_VERSION_39), QUIC_VERSION_39));
   EXPECT_TRUE(QuicUtils::IsConnectionIdValidForVersion(
       QuicUtils::CreateZeroConnectionId(QUIC_VERSION_99), QUIC_VERSION_99));
-  if (!QuicConnectionIdSupportsVariableLength(quic::Perspective::IS_SERVER) ||
-      !QuicConnectionIdSupportsVariableLength(quic::Perspective::IS_CLIENT)) {
-    return;
-  }
-  EXPECT_NE(QuicUtils::CreateZeroConnectionId(QUIC_VERSION_35),
+  EXPECT_NE(QuicUtils::CreateZeroConnectionId(QUIC_VERSION_39),
             EmptyQuicConnectionId());
   EXPECT_FALSE(QuicUtils::IsConnectionIdValidForVersion(EmptyQuicConnectionId(),
-                                                        QUIC_VERSION_35));
+                                                        QUIC_VERSION_39));
 }
 
 TEST_F(QuicUtilsTest, StatelessResetToken) {
diff --git a/quic/core/quic_version_manager.cc b/quic/core/quic_version_manager.cc
index 064e932..62b2e44 100644
--- a/quic/core/quic_version_manager.cc
+++ b/quic/core/quic_version_manager.cc
@@ -17,10 +17,9 @@
     : enable_version_99_(GetQuicReloadableFlag(quic_enable_version_99)),
       enable_version_47_(GetQuicReloadableFlag(quic_enable_version_47)),
       enable_version_46_(GetQuicReloadableFlag(quic_enable_version_46)),
-      enable_version_45_(GetQuicReloadableFlag(quic_enable_version_45)),
       enable_version_44_(GetQuicReloadableFlag(quic_enable_version_44)),
       enable_version_43_(GetQuicReloadableFlag(quic_enable_version_43)),
-      disable_version_35_(GetQuicReloadableFlag(quic_disable_version_35)),
+      disable_version_39_(GetQuicReloadableFlag(quic_disable_version_39)),
       allowed_supported_versions_(std::move(supported_versions)) {
   RefilterSupportedVersions();
 }
@@ -42,17 +41,15 @@
   if (enable_version_99_ != GetQuicReloadableFlag(quic_enable_version_99) ||
       enable_version_47_ != GetQuicReloadableFlag(quic_enable_version_47) ||
       enable_version_46_ != GetQuicReloadableFlag(quic_enable_version_46) ||
-      enable_version_45_ != GetQuicReloadableFlag(quic_enable_version_45) ||
       enable_version_44_ != GetQuicReloadableFlag(quic_enable_version_44) ||
       enable_version_43_ != GetQuicReloadableFlag(quic_enable_version_43) ||
-      disable_version_35_ != GetQuicReloadableFlag(quic_disable_version_35)) {
+      disable_version_39_ != GetQuicReloadableFlag(quic_disable_version_39)) {
     enable_version_99_ = GetQuicReloadableFlag(quic_enable_version_99);
     enable_version_47_ = GetQuicReloadableFlag(quic_enable_version_47);
     enable_version_46_ = GetQuicReloadableFlag(quic_enable_version_46);
-    enable_version_45_ = GetQuicReloadableFlag(quic_enable_version_45);
     enable_version_44_ = GetQuicReloadableFlag(quic_enable_version_44);
     enable_version_43_ = GetQuicReloadableFlag(quic_enable_version_43);
-    disable_version_35_ = GetQuicReloadableFlag(quic_disable_version_35);
+    disable_version_39_ = GetQuicReloadableFlag(quic_disable_version_39);
     RefilterSupportedVersions();
   }
 }
diff --git a/quic/core/quic_version_manager.h b/quic/core/quic_version_manager.h
index 1196c31..ab06868 100644
--- a/quic/core/quic_version_manager.h
+++ b/quic/core/quic_version_manager.h
@@ -41,14 +41,12 @@
   bool enable_version_47_;
   // quic_enable_version_46 flag
   bool enable_version_46_;
-  // quic_enable_version_45 flag
-  bool enable_version_45_;
   // quic_enable_version_44 flag
   bool enable_version_44_;
   // quic_enable_version_43 flag
   bool enable_version_43_;
-  // quic_disable_version_35 flag
-  bool disable_version_35_;
+  // quic_disable_version_39 flag
+  bool disable_version_39_;
   // The list of versions that may be supported.
   ParsedQuicVersionVector allowed_supported_versions_;
   // This vector contains QUIC versions which are currently supported based on
diff --git a/quic/core/quic_version_manager_test.cc b/quic/core/quic_version_manager_test.cc
index b734b89..7c7a08d 100644
--- a/quic/core/quic_version_manager_test.cc
+++ b/quic/core/quic_version_manager_test.cc
@@ -16,62 +16,50 @@
 class QuicVersionManagerTest : public QuicTest {};
 
 TEST_F(QuicVersionManagerTest, QuicVersionManager) {
-  static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 8u,
+  static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 6u,
                 "Supported versions out of sync");
   SetQuicReloadableFlag(quic_enable_version_99, false);
   SetQuicReloadableFlag(quic_enable_version_47, false);
   SetQuicReloadableFlag(quic_enable_version_46, false);
-  SetQuicReloadableFlag(quic_enable_version_45, false);
   SetQuicReloadableFlag(quic_enable_version_44, false);
   SetQuicReloadableFlag(quic_enable_version_43, false);
-  SetQuicReloadableFlag(quic_disable_version_35, true);
+  SetQuicReloadableFlag(quic_disable_version_39, true);
   QuicVersionManager manager(AllSupportedVersions());
 
   EXPECT_EQ(FilterSupportedTransportVersions(AllSupportedTransportVersions()),
             manager.GetSupportedTransportVersions());
 
+  EXPECT_TRUE(manager.GetSupportedTransportVersions().empty());
+
+  SetQuicReloadableFlag(quic_disable_version_39, false);
   EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_39}),
             manager.GetSupportedTransportVersions());
 
-  SetQuicReloadableFlag(quic_disable_version_35, false);
-  EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_39, QUIC_VERSION_35}),
-            manager.GetSupportedTransportVersions());
-
   SetQuicReloadableFlag(quic_enable_version_43, true);
-  EXPECT_EQ(QuicTransportVersionVector(
-                {QUIC_VERSION_43, QUIC_VERSION_39, QUIC_VERSION_35}),
+  EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_43, QUIC_VERSION_39}),
             manager.GetSupportedTransportVersions());
 
   SetQuicReloadableFlag(quic_enable_version_44, true);
-  EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_44, QUIC_VERSION_43,
-                                        QUIC_VERSION_39, QUIC_VERSION_35}),
-            manager.GetSupportedTransportVersions());
-
-  SetQuicReloadableFlag(quic_enable_version_45, true);
-  EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_45, QUIC_VERSION_44,
-                                        QUIC_VERSION_43, QUIC_VERSION_39,
-                                        QUIC_VERSION_35}),
+  EXPECT_EQ(QuicTransportVersionVector(
+                {QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39}),
             manager.GetSupportedTransportVersions());
 
   SetQuicReloadableFlag(quic_enable_version_46, true);
-  EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_46, QUIC_VERSION_45,
-                                        QUIC_VERSION_44, QUIC_VERSION_43,
-                                        QUIC_VERSION_39, QUIC_VERSION_35}),
+  EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_46, QUIC_VERSION_44,
+                                        QUIC_VERSION_43, QUIC_VERSION_39}),
             manager.GetSupportedTransportVersions());
 
   SetQuicReloadableFlag(quic_enable_version_47, true);
-  EXPECT_EQ(
-      QuicTransportVersionVector(
-          {QUIC_VERSION_47, QUIC_VERSION_46, QUIC_VERSION_45, QUIC_VERSION_44,
-           QUIC_VERSION_43, QUIC_VERSION_39, QUIC_VERSION_35}),
-      manager.GetSupportedTransportVersions());
+  EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_47, QUIC_VERSION_46,
+                                        QUIC_VERSION_44, QUIC_VERSION_43,
+                                        QUIC_VERSION_39}),
+            manager.GetSupportedTransportVersions());
 
   SetQuicReloadableFlag(quic_enable_version_99, true);
-  EXPECT_EQ(
-      QuicTransportVersionVector(
-          {QUIC_VERSION_99, QUIC_VERSION_47, QUIC_VERSION_46, QUIC_VERSION_45,
-           QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39, QUIC_VERSION_35}),
-      manager.GetSupportedTransportVersions());
+  EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_99, QUIC_VERSION_47,
+                                        QUIC_VERSION_46, QUIC_VERSION_44,
+                                        QUIC_VERSION_43, QUIC_VERSION_39}),
+            manager.GetSupportedTransportVersions());
 
   // Ensure that all versions are now supported.
   EXPECT_EQ(FilterSupportedTransportVersions(AllSupportedTransportVersions()),
diff --git a/quic/core/quic_versions.cc b/quic/core/quic_versions.cc
index 6f050dd..efc823d 100644
--- a/quic/core/quic_versions.cc
+++ b/quic/core/quic_versions.cc
@@ -54,16 +54,12 @@
       return 0;
   }
   switch (parsed_version.transport_version) {
-    case QUIC_VERSION_35:
-      return MakeVersionLabel(proto, '0', '3', '5');
     case QUIC_VERSION_39:
       return MakeVersionLabel(proto, '0', '3', '9');
     case QUIC_VERSION_43:
       return MakeVersionLabel(proto, '0', '4', '3');
     case QUIC_VERSION_44:
       return MakeVersionLabel(proto, '0', '4', '4');
-    case QUIC_VERSION_45:
-      return MakeVersionLabel(proto, '0', '4', '5');
     case QUIC_VERSION_46:
       return MakeVersionLabel(proto, '0', '4', '6');
     case QUIC_VERSION_47:
@@ -123,6 +119,11 @@
       continue;
     }
     for (QuicTransportVersion version : kSupportedTransportVersions) {
+      if (protocol == PROTOCOL_TLS1_3 && version < QUIC_VERSION_47) {
+        // The TLS handshake is only deployable if CRYPTO frames are also used,
+        // which are added in v47.
+        continue;
+      }
       supported_versions.push_back(ParsedQuicVersion(protocol, version));
     }
   }
@@ -163,7 +164,6 @@
       if (GetQuicReloadableFlag(quic_enable_version_99) &&
           GetQuicReloadableFlag(quic_enable_version_47) &&
           GetQuicReloadableFlag(quic_enable_version_46) &&
-          GetQuicReloadableFlag(quic_enable_version_45) &&
           GetQuicReloadableFlag(quic_enable_version_44) &&
           GetQuicReloadableFlag(quic_enable_version_43)) {
         filtered_versions.push_back(version);
@@ -171,20 +171,12 @@
     } else if (version.transport_version == QUIC_VERSION_47) {
       if (GetQuicReloadableFlag(quic_enable_version_47) &&
           GetQuicReloadableFlag(quic_enable_version_46) &&
-          GetQuicReloadableFlag(quic_enable_version_45) &&
           GetQuicReloadableFlag(quic_enable_version_44) &&
           GetQuicReloadableFlag(quic_enable_version_43)) {
         filtered_versions.push_back(version);
       }
     } else if (version.transport_version == QUIC_VERSION_46) {
       if (GetQuicReloadableFlag(quic_enable_version_46) &&
-          GetQuicReloadableFlag(quic_enable_version_45) &&
-          GetQuicReloadableFlag(quic_enable_version_44) &&
-          GetQuicReloadableFlag(quic_enable_version_43)) {
-        filtered_versions.push_back(version);
-      }
-    } else if (version.transport_version == QUIC_VERSION_45) {
-      if (GetQuicReloadableFlag(quic_enable_version_45) &&
           GetQuicReloadableFlag(quic_enable_version_44) &&
           GetQuicReloadableFlag(quic_enable_version_43)) {
         filtered_versions.push_back(version);
@@ -198,8 +190,8 @@
       if (GetQuicReloadableFlag(quic_enable_version_43)) {
         filtered_versions.push_back(version);
       }
-    } else if (version.transport_version == QUIC_VERSION_35) {
-      if (!GetQuicReloadableFlag(quic_disable_version_35)) {
+    } else if (version.transport_version == QUIC_VERSION_39) {
+      if (!GetQuicReloadableFlag(quic_disable_version_39)) {
         filtered_versions.push_back(version);
       }
     } else {
@@ -290,11 +282,9 @@
 
 QuicString QuicVersionToString(QuicTransportVersion transport_version) {
   switch (transport_version) {
-    RETURN_STRING_LITERAL(QUIC_VERSION_35);
     RETURN_STRING_LITERAL(QUIC_VERSION_39);
     RETURN_STRING_LITERAL(QUIC_VERSION_43);
     RETURN_STRING_LITERAL(QUIC_VERSION_44);
-    RETURN_STRING_LITERAL(QUIC_VERSION_45);
     RETURN_STRING_LITERAL(QUIC_VERSION_46);
     RETURN_STRING_LITERAL(QUIC_VERSION_47);
     RETURN_STRING_LITERAL(QUIC_VERSION_99);
diff --git a/quic/core/quic_versions.h b/quic/core/quic_versions.h
index 2c3ba1f..c0fa34a 100644
--- a/quic/core/quic_versions.h
+++ b/quic/core/quic_versions.h
@@ -76,9 +76,7 @@
   //            private flag from packet header and changed the ACK format to
   //            specify ranges of packets acknowledged rather than missing
   //            ranges.
-
-  QUIC_VERSION_35 = 35,  // Allows endpoints to independently set stream limit.
-
+  // Version 35 allows endpoints to independently set stream limit.
   // Version 36 added support for forced head-of-line blocking experiments.
   // Version 37 added perspective into null encryption.
   // Version 38 switched to IETF padding frame format and support for NSTP (no
@@ -100,10 +98,12 @@
   QUIC_VERSION_43 = 43,  // PRIORITY frames are sent by client and accepted by
                          // server.
   QUIC_VERSION_44 = 44,  // Use IETF header format.
-  QUIC_VERSION_45 = 45,  // Added MESSAGE frame.
-  QUIC_VERSION_46 = 46,  // Use CRYPTO frames for QuicCryptoStreams.
-  QUIC_VERSION_47 = 47,  // Use IETF draft-17 header format with demultiplexing
+
+  // Version 45 added MESSAGE frame.
+
+  QUIC_VERSION_46 = 46,  // Use IETF draft-17 header format with demultiplexing
                          // bit.
+  QUIC_VERSION_47 = 47,  // Use CRYPTO frames for QuicCryptoStreams.
   QUIC_VERSION_99 = 99,  // Dumping ground for IETF QUIC changes which are not
                          // yet ready for production.
 };
@@ -166,8 +166,9 @@
 //
 // See go/new-quic-version for more details on how to roll out new versions.
 static const QuicTransportVersion kSupportedTransportVersions[] = {
-    QUIC_VERSION_99, QUIC_VERSION_47, QUIC_VERSION_46, QUIC_VERSION_45,
-    QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39, QUIC_VERSION_35};
+    QUIC_VERSION_99, QUIC_VERSION_47, QUIC_VERSION_46,
+    QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39,
+};
 
 // This vector contains all crypto handshake protocols that are supported.
 static const HandshakeProtocol kSupportedHandshakeProtocols[] = {
@@ -308,6 +309,38 @@
                                          std::numeric_limits<size_t>::max());
 }
 
+// Returns true if QuicSpdyStream encodes body using HTTP/3 specification and
+// sends data frame header along with body.
+QUIC_EXPORT_PRIVATE inline bool VersionHasDataFrameHeader(
+    QuicTransportVersion transport_version) {
+  return transport_version == QUIC_VERSION_99;
+}
+
+// Returns true if QuicSpdySession instantiates a QPACK encoder and decoder.
+// TODO(123528590): Implement the following features and gate them on this
+// function as well, optionally renaming this function as appropriate.
+// Send HEADERS on the request/response stream instead of the headers stream.
+// Send PUSH_PROMISE on the request/response stream instead of headers stream.
+// Send PRIORITY on the request/response stream instead of the headers stream.
+// Do not instantiate the headers stream object.
+QUIC_EXPORT_PRIVATE inline bool VersionUsesQpack(
+    QuicTransportVersion transport_version) {
+  const bool uses_qpack = (transport_version == QUIC_VERSION_99);
+  if (uses_qpack) {
+    DCHECK(VersionHasDataFrameHeader(transport_version));
+  }
+  return uses_qpack;
+}
+
+// Returns whether the transport_version supports the variable length integer
+// length field as defined by IETF QUIC draft-13 and later.
+QUIC_EXPORT_PRIVATE inline bool QuicVersionHasLongHeaderLengths(
+    QuicTransportVersion transport_version) {
+  // TODO(dschinazi) if we enable long header lengths before v99, we need to
+  // add support for fixing up lengths in QuicFramer::BuildDataPacket.
+  return transport_version == QUIC_VERSION_99;
+}
+
 }  // namespace quic
 
 #endif  // QUICHE_QUIC_CORE_QUIC_VERSIONS_H_
diff --git a/quic/core/quic_versions_test.cc b/quic/core/quic_versions_test.cc
index a0f664e..db7f53a 100644
--- a/quic/core/quic_versions_test.cc
+++ b/quic/core/quic_versions_test.cc
@@ -34,8 +34,8 @@
   log.StartCapturingLogs();
 
   // Explicitly test a specific version.
-  EXPECT_EQ(MakeQuicTag('5', '3', '0', 'Q'),
-            QuicVersionToQuicVersionLabel(QUIC_VERSION_35));
+  EXPECT_EQ(MakeQuicTag('9', '3', '0', 'Q'),
+            QuicVersionToQuicVersionLabel(QUIC_VERSION_39));
 
   // Loop over all supported versions and make sure that we never hit the
   // default case (i.e. all supported versions should be successfully converted
@@ -72,8 +72,8 @@
   log.StartCapturingLogs();
 
   // Explicitly test specific versions.
-  EXPECT_EQ(QUIC_VERSION_35,
-            QuicVersionLabelToQuicVersion(MakeQuicTag('5', '3', '0', 'Q')));
+  EXPECT_EQ(QUIC_VERSION_39,
+            QuicVersionLabelToQuicVersion(MakeQuicTag('9', '3', '0', 'Q')));
 
   for (size_t i = 0; i < QUIC_ARRAYSIZE(kSupportedTransportVersions); ++i) {
     QuicTransportVersion version = kSupportedTransportVersions[i];
@@ -130,16 +130,12 @@
 }
 
 TEST_F(QuicVersionsTest, ParseQuicVersionLabel) {
-  EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_35),
-            ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '3', '5')));
   EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_39),
             ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '3', '9')));
   EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43),
             ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '4', '3')));
   EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44),
             ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '4', '4')));
-  EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_45),
-            ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '4', '5')));
   EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46),
             ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '4', '6')));
   EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_47),
@@ -147,16 +143,12 @@
 
   // Test a TLS version:
   FLAGS_quic_supports_tls_handshake = true;
-  EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_35),
-            ParseQuicVersionLabel(MakeVersionLabel('T', '0', '3', '5')));
   EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_39),
             ParseQuicVersionLabel(MakeVersionLabel('T', '0', '3', '9')));
   EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_43),
             ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '3')));
   EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_44),
             ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '4')));
-  EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_45),
-            ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '5')));
   EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_46),
             ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '6')));
   EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_47),
@@ -180,9 +172,6 @@
 }
 
 TEST_F(QuicVersionsTest, CreateQuicVersionLabel) {
-  EXPECT_EQ(MakeVersionLabel('Q', '0', '3', '5'),
-            CreateQuicVersionLabel(
-                ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_35)));
   EXPECT_EQ(MakeVersionLabel('Q', '0', '3', '9'),
             CreateQuicVersionLabel(
                 ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_39)));
@@ -192,9 +181,6 @@
   EXPECT_EQ(MakeVersionLabel('Q', '0', '4', '4'),
             CreateQuicVersionLabel(
                 ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44)));
-  EXPECT_EQ(MakeVersionLabel('Q', '0', '4', '5'),
-            CreateQuicVersionLabel(
-                ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_45)));
   EXPECT_EQ(MakeVersionLabel('Q', '0', '4', '6'),
             CreateQuicVersionLabel(
                 ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46)));
@@ -204,9 +190,6 @@
 
   // Test a TLS version:
   FLAGS_quic_supports_tls_handshake = true;
-  EXPECT_EQ(MakeVersionLabel('T', '0', '3', '5'),
-            CreateQuicVersionLabel(
-                ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_35)));
   EXPECT_EQ(MakeVersionLabel('T', '0', '3', '9'),
             CreateQuicVersionLabel(
                 ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_39)));
@@ -216,9 +199,6 @@
   EXPECT_EQ(MakeVersionLabel('T', '0', '4', '4'),
             CreateQuicVersionLabel(
                 ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_44)));
-  EXPECT_EQ(MakeVersionLabel('T', '0', '4', '5'),
-            CreateQuicVersionLabel(
-                ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_45)));
   EXPECT_EQ(MakeVersionLabel('T', '0', '4', '6'),
             CreateQuicVersionLabel(
                 ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_46)));
@@ -236,8 +216,6 @@
   EXPECT_EQ(UnsupportedQuicVersion(),
             ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '4')));
   EXPECT_EQ(UnsupportedQuicVersion(),
-            ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '5')));
-  EXPECT_EQ(UnsupportedQuicVersion(),
             ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '6')));
   EXPECT_EQ(UnsupportedQuicVersion(),
             ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '7')));
@@ -261,25 +239,25 @@
 }
 
 TEST_F(QuicVersionsTest, QuicVersionToString) {
-  EXPECT_EQ("QUIC_VERSION_35", QuicVersionToString(QUIC_VERSION_35));
+  EXPECT_EQ("QUIC_VERSION_39", QuicVersionToString(QUIC_VERSION_39));
   EXPECT_EQ("QUIC_VERSION_UNSUPPORTED",
             QuicVersionToString(QUIC_VERSION_UNSUPPORTED));
 
-  QuicTransportVersion single_version[] = {QUIC_VERSION_35};
+  QuicTransportVersion single_version[] = {QUIC_VERSION_39};
   QuicTransportVersionVector versions_vector;
   for (size_t i = 0; i < QUIC_ARRAYSIZE(single_version); ++i) {
     versions_vector.push_back(single_version[i]);
   }
-  EXPECT_EQ("QUIC_VERSION_35",
+  EXPECT_EQ("QUIC_VERSION_39",
             QuicTransportVersionVectorToString(versions_vector));
 
   QuicTransportVersion multiple_versions[] = {QUIC_VERSION_UNSUPPORTED,
-                                              QUIC_VERSION_35};
+                                              QUIC_VERSION_39};
   versions_vector.clear();
   for (size_t i = 0; i < QUIC_ARRAYSIZE(multiple_versions); ++i) {
     versions_vector.push_back(multiple_versions[i]);
   }
-  EXPECT_EQ("QUIC_VERSION_UNSUPPORTED,QUIC_VERSION_35",
+  EXPECT_EQ("QUIC_VERSION_UNSUPPORTED,QUIC_VERSION_39",
             QuicTransportVersionVectorToString(versions_vector));
 
   // Make sure that all supported versions are present in QuicVersionToString.
@@ -291,16 +269,16 @@
 
 TEST_F(QuicVersionsTest, ParsedQuicVersionToString) {
   ParsedQuicVersion unsupported = UnsupportedQuicVersion();
-  ParsedQuicVersion version35(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_35);
-  EXPECT_EQ("Q035", ParsedQuicVersionToString(version35));
+  ParsedQuicVersion version39(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_39);
+  EXPECT_EQ("Q039", ParsedQuicVersionToString(version39));
   EXPECT_EQ("0", ParsedQuicVersionToString(unsupported));
 
-  ParsedQuicVersionVector versions_vector = {version35};
-  EXPECT_EQ("Q035", ParsedQuicVersionVectorToString(versions_vector));
+  ParsedQuicVersionVector versions_vector = {version39};
+  EXPECT_EQ("Q039", ParsedQuicVersionVectorToString(versions_vector));
 
-  versions_vector = {unsupported, version35};
-  EXPECT_EQ("0,Q035", ParsedQuicVersionVectorToString(versions_vector));
-  EXPECT_EQ("0:Q035", ParsedQuicVersionVectorToString(versions_vector, ":",
+  versions_vector = {unsupported, version39};
+  EXPECT_EQ("0,Q039", ParsedQuicVersionVectorToString(versions_vector));
+  EXPECT_EQ("0:Q039", ParsedQuicVersionVectorToString(versions_vector, ":",
                                                       versions_vector.size()));
   EXPECT_EQ("0|...", ParsedQuicVersionVectorToString(versions_vector, "|", 0));
 
@@ -324,10 +302,9 @@
 
 TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsAllVersions) {
   QuicTransportVersionVector all_versions = AllSupportedTransportVersions();
-  SetQuicReloadableFlag(quic_disable_version_35, false);
+  SetQuicReloadableFlag(quic_disable_version_39, false);
   SetQuicReloadableFlag(quic_enable_version_43, true);
   SetQuicReloadableFlag(quic_enable_version_44, true);
-  SetQuicReloadableFlag(quic_enable_version_45, true);
   SetQuicReloadableFlag(quic_enable_version_46, true);
   SetQuicReloadableFlag(quic_enable_version_47, true);
   SetQuicReloadableFlag(quic_enable_version_99, true);
@@ -336,8 +313,8 @@
     parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
   }
   QuicTransportVersionVector expected_versions = {
-      QUIC_VERSION_99, QUIC_VERSION_47, QUIC_VERSION_46, QUIC_VERSION_45,
-      QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39, QUIC_VERSION_35};
+      QUIC_VERSION_99, QUIC_VERSION_47, QUIC_VERSION_46,
+      QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39};
   ParsedQuicVersionVector expected_parsed_versions;
   for (QuicTransportVersion version : expected_versions) {
     expected_parsed_versions.push_back(
@@ -350,10 +327,9 @@
 
 TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo99) {
   QuicTransportVersionVector all_versions = AllSupportedTransportVersions();
-  SetQuicReloadableFlag(quic_disable_version_35, false);
+  SetQuicReloadableFlag(quic_disable_version_39, false);
   SetQuicReloadableFlag(quic_enable_version_43, true);
   SetQuicReloadableFlag(quic_enable_version_44, true);
-  SetQuicReloadableFlag(quic_enable_version_45, true);
   SetQuicReloadableFlag(quic_enable_version_46, true);
   SetQuicReloadableFlag(quic_enable_version_47, true);
   SetQuicReloadableFlag(quic_enable_version_99, false);
@@ -362,8 +338,8 @@
     parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
   }
   QuicTransportVersionVector expected_versions = {
-      QUIC_VERSION_47, QUIC_VERSION_46, QUIC_VERSION_45, QUIC_VERSION_44,
-      QUIC_VERSION_43, QUIC_VERSION_39, QUIC_VERSION_35};
+      QUIC_VERSION_47, QUIC_VERSION_46, QUIC_VERSION_44, QUIC_VERSION_43,
+      QUIC_VERSION_39};
   ParsedQuicVersionVector expected_parsed_versions;
   for (QuicTransportVersion version : expected_versions) {
     expected_parsed_versions.push_back(
@@ -376,10 +352,9 @@
 
 TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo47) {
   QuicTransportVersionVector all_versions = AllSupportedTransportVersions();
-  SetQuicReloadableFlag(quic_disable_version_35, false);
+  SetQuicReloadableFlag(quic_disable_version_39, false);
   SetQuicReloadableFlag(quic_enable_version_43, true);
   SetQuicReloadableFlag(quic_enable_version_44, true);
-  SetQuicReloadableFlag(quic_enable_version_45, true);
   SetQuicReloadableFlag(quic_enable_version_46, true);
   SetQuicReloadableFlag(quic_enable_version_47, false);
   SetQuicReloadableFlag(quic_enable_version_99, false);
@@ -388,8 +363,7 @@
     parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
   }
   QuicTransportVersionVector expected_versions = {
-      QUIC_VERSION_46, QUIC_VERSION_45, QUIC_VERSION_44,
-      QUIC_VERSION_43, QUIC_VERSION_39, QUIC_VERSION_35};
+      QUIC_VERSION_46, QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39};
   ParsedQuicVersionVector expected_parsed_versions;
   for (QuicTransportVersion version : expected_versions) {
     expected_parsed_versions.push_back(
@@ -402,10 +376,9 @@
 
 TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo46) {
   QuicTransportVersionVector all_versions = AllSupportedTransportVersions();
-  SetQuicReloadableFlag(quic_disable_version_35, false);
+  SetQuicReloadableFlag(quic_disable_version_39, false);
   SetQuicReloadableFlag(quic_enable_version_43, true);
   SetQuicReloadableFlag(quic_enable_version_44, true);
-  SetQuicReloadableFlag(quic_enable_version_45, true);
   SetQuicReloadableFlag(quic_enable_version_46, false);
   SetQuicReloadableFlag(quic_enable_version_47, false);
   SetQuicReloadableFlag(quic_enable_version_99, false);
@@ -414,33 +387,7 @@
     parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
   }
   QuicTransportVersionVector expected_versions = {
-      QUIC_VERSION_45, QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39,
-      QUIC_VERSION_35};
-  ParsedQuicVersionVector expected_parsed_versions;
-  for (QuicTransportVersion version : expected_versions) {
-    expected_parsed_versions.push_back(
-        ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
-  }
-
-  ASSERT_EQ(expected_versions, FilterSupportedTransportVersions(all_versions));
-  ASSERT_EQ(expected_parsed_versions, FilterSupportedVersions(parsed_versions));
-}
-
-TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo45) {
-  QuicTransportVersionVector all_versions = AllSupportedTransportVersions();
-  SetQuicReloadableFlag(quic_disable_version_35, false);
-  SetQuicReloadableFlag(quic_enable_version_43, true);
-  SetQuicReloadableFlag(quic_enable_version_44, true);
-  SetQuicReloadableFlag(quic_enable_version_45, false);
-  SetQuicReloadableFlag(quic_enable_version_46, false);
-  SetQuicReloadableFlag(quic_enable_version_47, false);
-  SetQuicReloadableFlag(quic_enable_version_99, false);
-  ParsedQuicVersionVector parsed_versions;
-  for (QuicTransportVersion version : all_versions) {
-    parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
-  }
-  QuicTransportVersionVector expected_versions = {
-      QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39, QUIC_VERSION_35};
+      QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39};
   ParsedQuicVersionVector expected_parsed_versions;
   for (QuicTransportVersion version : expected_versions) {
     expected_parsed_versions.push_back(
@@ -453,10 +400,9 @@
 
 TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo44) {
   QuicTransportVersionVector all_versions = AllSupportedTransportVersions();
-  SetQuicReloadableFlag(quic_disable_version_35, false);
+  SetQuicReloadableFlag(quic_disable_version_39, false);
   SetQuicReloadableFlag(quic_enable_version_43, true);
   SetQuicReloadableFlag(quic_enable_version_44, false);
-  SetQuicReloadableFlag(quic_enable_version_45, false);
   SetQuicReloadableFlag(quic_enable_version_46, false);
   SetQuicReloadableFlag(quic_enable_version_47, false);
   SetQuicReloadableFlag(quic_enable_version_99, false);
@@ -464,8 +410,8 @@
   for (QuicTransportVersion version : all_versions) {
     parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
   }
-  QuicTransportVersionVector expected_versions = {
-      QUIC_VERSION_43, QUIC_VERSION_39, QUIC_VERSION_35};
+  QuicTransportVersionVector expected_versions = {QUIC_VERSION_43,
+                                                  QUIC_VERSION_39};
   ParsedQuicVersionVector expected_parsed_versions;
   for (QuicTransportVersion version : expected_versions) {
     expected_parsed_versions.push_back(
@@ -478,35 +424,9 @@
 
 TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo43) {
   QuicTransportVersionVector all_versions = AllSupportedTransportVersions();
-  SetQuicReloadableFlag(quic_disable_version_35, false);
+  SetQuicReloadableFlag(quic_disable_version_39, false);
   SetQuicReloadableFlag(quic_enable_version_43, false);
   SetQuicReloadableFlag(quic_enable_version_44, false);
-  SetQuicReloadableFlag(quic_enable_version_45, false);
-  SetQuicReloadableFlag(quic_enable_version_46, false);
-  SetQuicReloadableFlag(quic_enable_version_47, false);
-  SetQuicReloadableFlag(quic_enable_version_99, false);
-  ParsedQuicVersionVector parsed_versions;
-  for (QuicTransportVersion version : all_versions) {
-    parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
-  }
-  QuicTransportVersionVector expected_versions = {QUIC_VERSION_39,
-                                                  QUIC_VERSION_35};
-  ParsedQuicVersionVector expected_parsed_versions;
-  for (QuicTransportVersion version : expected_versions) {
-    expected_parsed_versions.push_back(
-        ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
-  }
-
-  ASSERT_EQ(expected_versions, FilterSupportedTransportVersions(all_versions));
-  ASSERT_EQ(expected_parsed_versions, FilterSupportedVersions(parsed_versions));
-}
-
-TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo35) {
-  QuicTransportVersionVector all_versions = AllSupportedTransportVersions();
-  SetQuicReloadableFlag(quic_disable_version_35, true);
-  SetQuicReloadableFlag(quic_enable_version_43, false);
-  SetQuicReloadableFlag(quic_enable_version_44, false);
-  SetQuicReloadableFlag(quic_enable_version_45, false);
   SetQuicReloadableFlag(quic_enable_version_46, false);
   SetQuicReloadableFlag(quic_enable_version_47, false);
   SetQuicReloadableFlag(quic_enable_version_99, false);
@@ -525,8 +445,31 @@
   ASSERT_EQ(expected_parsed_versions, FilterSupportedVersions(parsed_versions));
 }
 
+TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo39) {
+  QuicTransportVersionVector all_versions = AllSupportedTransportVersions();
+  SetQuicReloadableFlag(quic_disable_version_39, true);
+  SetQuicReloadableFlag(quic_enable_version_43, true);
+  SetQuicReloadableFlag(quic_enable_version_44, false);
+  SetQuicReloadableFlag(quic_enable_version_46, false);
+  SetQuicReloadableFlag(quic_enable_version_47, false);
+  SetQuicReloadableFlag(quic_enable_version_99, false);
+  ParsedQuicVersionVector parsed_versions;
+  for (QuicTransportVersion version : all_versions) {
+    parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
+  }
+  QuicTransportVersionVector expected_versions = {QUIC_VERSION_43};
+  ParsedQuicVersionVector expected_parsed_versions;
+  for (QuicTransportVersion version : expected_versions) {
+    expected_parsed_versions.push_back(
+        ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
+  }
+
+  ASSERT_EQ(expected_versions, FilterSupportedTransportVersions(all_versions));
+  ASSERT_EQ(expected_parsed_versions, FilterSupportedVersions(parsed_versions));
+}
+
 TEST_F(QuicVersionsTest, LookUpVersionByIndex) {
-  QuicTransportVersionVector all_versions = {QUIC_VERSION_35, QUIC_VERSION_39};
+  QuicTransportVersionVector all_versions = {QUIC_VERSION_39};
   int version_count = all_versions.size();
   for (int i = -5; i <= version_count + 1; ++i) {
     if (i >= 0 && i < version_count) {
@@ -564,13 +507,11 @@
 // yet a typo was made in doing the #defines and it was caught
 // only in some test far removed from here... Better safe than sorry.
 TEST_F(QuicVersionsTest, CheckVersionNumbersForTypos) {
-  static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 8u,
+  static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 6u,
                 "Supported versions out of sync");
-  EXPECT_EQ(QUIC_VERSION_35, 35);
   EXPECT_EQ(QUIC_VERSION_39, 39);
   EXPECT_EQ(QUIC_VERSION_43, 43);
   EXPECT_EQ(QUIC_VERSION_44, 44);
-  EXPECT_EQ(QUIC_VERSION_45, 45);
   EXPECT_EQ(QUIC_VERSION_46, 46);
   EXPECT_EQ(QUIC_VERSION_47, 47);
   EXPECT_EQ(QUIC_VERSION_99, 99);
diff --git a/quic/core/stateless_rejector_test.cc b/quic/core/stateless_rejector_test.cc
index 497fe54..19eef64 100644
--- a/quic/core/stateless_rejector_test.cc
+++ b/quic/core/stateless_rejector_test.cc
@@ -169,10 +169,9 @@
   QuicString stk_hex_;
 };
 
-INSTANTIATE_TEST_CASE_P(Flags,
-                        StatelessRejectorTest,
-                        ::testing::ValuesIn(GetTestParams()),
-                        TestParamToString);
+INSTANTIATE_TEST_SUITE_P(Flags, StatelessRejectorTest,
+                         ::testing::ValuesIn(GetTestParams()),
+                         TestParamToString);
 
 TEST_P(StatelessRejectorTest, InvalidChlo) {
   // clang-format off
diff --git a/quic/core/tls_handshaker.cc b/quic/core/tls_handshaker.cc
index a471aa4..e2efa0f 100644
--- a/quic/core/tls_handshaker.cc
+++ b/quic/core/tls_handshaker.cc
@@ -108,7 +108,7 @@
       return ENCRYPTION_NONE;
     case ssl_encryption_early_data:
     case ssl_encryption_handshake:
-      return ENCRYPTION_INITIAL;
+      return ENCRYPTION_ZERO_RTT;
     case ssl_encryption_application:
       return ENCRYPTION_FORWARD_SECURE;
   }
@@ -120,7 +120,7 @@
   switch (level) {
     case ENCRYPTION_NONE:
       return ssl_encryption_initial;
-    case ENCRYPTION_INITIAL:
+    case ENCRYPTION_ZERO_RTT:
       return ssl_encryption_handshake;
     case ENCRYPTION_FORWARD_SECURE:
       return ssl_encryption_application;
@@ -214,7 +214,7 @@
     // alternative decrypter instead of the primary decrypter. One reason for
     // this is that after the forward secure keys become available, the server
     // still has crypto handshake messages to read at the handshake encryption
-    // level, meaning that both the ENCRYPTION_INITIAL and
+    // level, meaning that both the ENCRYPTION_ZERO_RTT and
     // ENCRYPTION_FORWARD_SECURE decrypters need to be available. (Tests also
     // assume that an alternative decrypter gets set, so at some point we need
     // to call SetAlternativeDecrypter.)
diff --git a/quic/core/uber_quic_stream_id_manager_test.cc b/quic/core/uber_quic_stream_id_manager_test.cc
index a413a0b..353e35d 100644
--- a/quic/core/uber_quic_stream_id_manager_test.cc
+++ b/quic/core/uber_quic_stream_id_manager_test.cc
@@ -67,10 +67,9 @@
   QuicFrame frame_;
 };
 
-INSTANTIATE_TEST_CASE_P(Tests,
-                        UberQuicStreamIdManagerTest,
-                        ::testing::ValuesIn({Perspective::IS_CLIENT,
-                                             Perspective::IS_SERVER}));
+INSTANTIATE_TEST_SUITE_P(Tests, UberQuicStreamIdManagerTest,
+                         ::testing::ValuesIn({Perspective::IS_CLIENT,
+                                              Perspective::IS_SERVER}));
 
 TEST_P(UberQuicStreamIdManagerTest, Initialization) {
   if (GetParam() == Perspective::IS_SERVER) {
diff --git a/quic/platform/api/quic_containers.h b/quic/platform/api/quic_containers.h
index 04c462e..7cfb311 100644
--- a/quic/platform/api/quic_containers.h
+++ b/quic/platform/api/quic_containers.h
@@ -33,11 +33,6 @@
 template <typename Key, typename Value, int Size>
 using QuicSmallMap = QuicSmallMapImpl<Key, Value, Size>;
 
-// A data structure used to represent a sorted set of non-empty, non-adjacent,
-// and mutually disjoint intervals.
-template <typename T>
-using QuicIntervalSet = QuicIntervalSetImpl<T>;
-
 // Represents a simple queue which may be backed by a list or
 // a flat circular buffer.
 //
diff --git a/quic/platform/api/quic_default_proof_providers.h b/quic/platform/api/quic_default_proof_providers.h
new file mode 100644
index 0000000..ab683c1
--- /dev/null
+++ b/quic/platform/api/quic_default_proof_providers.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_DEFAULT_PROOF_PROVIDERS_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_DEFAULT_PROOF_PROVIDERS_H_
+
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h"
+#include "net/quic/platform/impl/quic_default_proof_providers_impl.h"
+
+namespace quic {
+
+// Provides a default proof verifier.  The verifier has to do a good faith
+// attempt at verifying the certificate against a reasonable root store, and not
+// just always return success.
+std::unique_ptr<ProofVerifier> CreateDefaultProofVerifier() {
+  return CreateDefaultProofVerifierImpl();
+}
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_PLATFORM_API_QUIC_DEFAULT_PROOF_PROVIDERS_H_
diff --git a/quic/platform/api/quic_epoll.h b/quic/platform/api/quic_epoll.h
index c289ef4..e429ef8 100644
--- a/quic/platform/api/quic_epoll.h
+++ b/quic/platform/api/quic_epoll.h
@@ -1,3 +1,7 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
 #ifndef QUICHE_QUIC_PLATFORM_API_QUIC_EPOLL_H_
 #define QUICHE_QUIC_PLATFORM_API_QUIC_EPOLL_H_
 
diff --git a/quic/platform/api/quic_error_code_wrappers.h b/quic/platform/api/quic_error_code_wrappers.h
new file mode 100644
index 0000000..733572e
--- /dev/null
+++ b/quic/platform/api/quic_error_code_wrappers.h
@@ -0,0 +1,14 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_ERROR_CODE_WRAPPERS_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_ERROR_CODE_WRAPPERS_H_
+
+#include "net/quic/platform/impl/quic_error_code_wrappers_impl.h"
+
+// TODO(vasilvv): ensure WRITE_STATUS_MSG_TOO_BIG works everywhere and remove
+// this.
+#define QUIC_EMSGSIZE QUIC_EMSGSIZE_IMPL
+
+#endif  // QUICHE_QUIC_PLATFORM_API_QUIC_ERROR_CODE_WRAPPERS_H_
diff --git a/quic/platform/api/quic_exported_stats.h b/quic/platform/api/quic_exported_stats.h
index b911c70..28c82ec 100644
--- a/quic/platform/api/quic_exported_stats.h
+++ b/quic/platform/api/quic_exported_stats.h
@@ -64,9 +64,9 @@
 #define QUIC_HISTOGRAM_TIMES(name, sample, min, max, bucket_count, docstring) \
   do {                                                                        \
     QUIC_CLIENT_HISTOGRAM_TIMES_IMPL(name, sample, min, max, bucket_count,    \
-                                     docstring)                               \
+                                     docstring);                              \
     QUIC_SERVER_HISTOGRAM_TIMES_IMPL(name, sample, min, max, bucket_count,    \
-                                     docstring)                               \
+                                     docstring);                              \
   } while (0)
 
 //------------------------------------------------------------------------------
diff --git a/quic/platform/api/quic_flags.h b/quic/platform/api/quic_flags.h
index 6ddc13a..45145a7 100644
--- a/quic/platform/api/quic_flags.h
+++ b/quic/platform/api/quic_flags.h
@@ -5,8 +5,16 @@
 #ifndef QUICHE_QUIC_PLATFORM_API_QUIC_FLAGS_H_
 #define QUICHE_QUIC_PLATFORM_API_QUIC_FLAGS_H_
 
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
 #include "net/quic/platform/impl/quic_flags_impl.h"
 
+// Define a command-line flag that can be automatically set via
+// QuicParseCommandLineFlags().
+#define DEFINE_QUIC_COMMAND_LINE_FLAG(type, name, default_value, help) \
+  DEFINE_QUIC_COMMAND_LINE_FLAG_IMPL(type, name, default_value, help)
+
 #define GetQuicReloadableFlag(flag) GetQuicReloadableFlagImpl(flag)
 #define SetQuicReloadableFlag(flag, value) \
   SetQuicReloadableFlagImpl(flag, value)
@@ -15,4 +23,26 @@
 #define GetQuicFlag(flag) GetQuicFlagImpl(flag)
 #define SetQuicFlag(flag, value) SetQuicFlagImpl(flag, value)
 
+namespace quic {
+
+// Parses command-line flags, setting flag variables defined using
+// DEFINE_QUIC_COMMAND_LINE_FLAG if they appear in the command line, and
+// returning a list of any non-flag arguments specified in the command line. If
+// the command line specifies '-h' or '--help', prints a usage message with flag
+// descriptions to stdout and exits with status 0. If a flag has an unparsable
+// value, writes an error message to stderr and exits with status 1.
+inline std::vector<QuicString> QuicParseCommandLineFlags(
+    const char* usage,
+    int argc,
+    const char* const* argv) {
+  return QuicParseCommandLineFlagsImpl(usage, argc, argv);
+}
+
+// Prints a usage message with flag descriptions to stdout.
+inline void QuicPrintCommandLineFlagHelp(const char* usage) {
+  QuicPrintCommandLineFlagHelpImpl(usage);
+}
+
+}  // namespace quic
+
 #endif  // QUICHE_QUIC_PLATFORM_API_QUIC_FLAGS_H_
diff --git a/quic/platform/api/quic_interval.h b/quic/platform/api/quic_interval.h
deleted file mode 100644
index 51d2f52..0000000
--- a/quic/platform/api/quic_interval.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_INTERVAL_H_
-#define QUICHE_QUIC_PLATFORM_API_QUIC_INTERVAL_H_
-
-#include "net/quic/platform/impl/quic_interval_impl.h"
-
-namespace quic {
-
-template <class T>
-using QuicInterval = QuicIntervalImpl<T>;
-
-}  // namespace quic
-
-#endif  // QUICHE_QUIC_PLATFORM_API_QUIC_INTERVAL_H_
diff --git a/quic/platform/api/quic_macros.h b/quic/platform/api/quic_macros.h
new file mode 100644
index 0000000..9dee2e9
--- /dev/null
+++ b/quic/platform/api/quic_macros.h
@@ -0,0 +1,13 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_MACROS_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_MACROS_H_
+
+#include "net/quic/platform/impl/quic_macros_impl.h"
+
+#define QUIC_MUST_USE_RESULT QUIC_MUST_USE_RESULT_IMPL
+#define QUIC_UNUSED QUIC_UNUSED_IMPL
+
+#endif  // QUICHE_QUIC_PLATFORM_API_QUIC_MACROS_H_
diff --git a/quic/platform/api/quic_mem_slice_span.h b/quic/platform/api/quic_mem_slice_span.h
index 39436be..781382e 100644
--- a/quic/platform/api/quic_mem_slice_span.h
+++ b/quic/platform/api/quic_mem_slice_span.h
@@ -35,6 +35,12 @@
     return impl_.SaveMemSlicesInSendBuffer(send_buffer);
   }
 
+  // Save data buffers as message data in |message_frame|. |message_frame| will
+  // hold a reference to all data buffers.
+  void SaveMemSlicesAsMessageData(QuicMessageFrame* message_frame) {
+    impl_.SaveMemSlicesAsMessageData(message_frame);
+  }
+
   // Return data of the span at |index| by the form of a QuicStringPiece.
   QuicStringPiece GetData(int index) { return impl_.GetData(index); }
 
diff --git a/quic/platform/api/quic_mem_slice_storage_test.cc b/quic/platform/api/quic_mem_slice_storage_test.cc
index d5a5b8f..72623e1 100644
--- a/quic/platform/api/quic_mem_slice_storage_test.cc
+++ b/quic/platform/api/quic_mem_slice_storage_test.cc
@@ -4,7 +4,6 @@
 
 #include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h"
 #include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
-
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
 
 namespace quic {
diff --git a/quic/platform/api/quic_port_utils.h b/quic/platform/api/quic_port_utils.h
new file mode 100644
index 0000000..aa85fc5
--- /dev/null
+++ b/quic/platform/api/quic_port_utils.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_PORT_UTILS_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_PORT_UTILS_H_
+
+#include "net/quic/platform/impl/quic_port_utils_impl.h"
+
+namespace quic {
+
+// Returns a UDP port that is currently unused.  Check-fails if none are
+// available.
+inline int QuicPickUnusedPortOrDie() {
+  return QuicPickUnusedPortOrDieImpl();
+}
+
+// Indicates that a specified port previously returned by
+// QuicPickUnusedPortOrDie is no longer used.
+inline void QuicRecyclePort(int port) {
+  return QuicRecyclePortImpl(port);
+}
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_PLATFORM_API_QUIC_PORT_UTILS_H_
diff --git a/quic/quartc/quartc_connection_helper.cc b/quic/quartc/quartc_connection_helper.cc
new file mode 100644
index 0000000..6c6ee97
--- /dev/null
+++ b/quic/quartc/quartc_connection_helper.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h"
+
+namespace quic {
+
+QuartcConnectionHelper::QuartcConnectionHelper(const QuicClock* clock)
+    : clock_(clock) {}
+
+const QuicClock* QuartcConnectionHelper::GetClock() const {
+  return clock_;
+}
+
+QuicRandom* QuartcConnectionHelper::GetRandomGenerator() {
+  return QuicRandom::GetInstance();
+}
+
+QuicBufferAllocator* QuartcConnectionHelper::GetStreamSendBufferAllocator() {
+  return &buffer_allocator_;
+}
+
+}  // namespace quic
diff --git a/quic/quartc/quartc_connection_helper.h b/quic/quartc/quartc_connection_helper.h
new file mode 100644
index 0000000..2b1d62f
--- /dev/null
+++ b/quic/quartc/quartc_connection_helper.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_QUARTC_QUARTC_CONNECTION_HELPER_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_CONNECTION_HELPER_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+
+namespace quic {
+
+// Simple implementation of QuicConnectionHelperInterface for Quartc.
+class QuartcConnectionHelper : public QuicConnectionHelperInterface {
+ public:
+  explicit QuartcConnectionHelper(const QuicClock* clock);
+
+  // QuicConnectionHelperInterface overrides.
+  const QuicClock* GetClock() const override;
+  QuicRandom* GetRandomGenerator() override;
+  QuicBufferAllocator* GetStreamSendBufferAllocator() override;
+
+ private:
+  const QuicClock* clock_;
+  SimpleBufferAllocator buffer_allocator_;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_QUARTC_QUARTC_CONNECTION_HELPER_H_
diff --git a/quic/quartc/quartc_crypto_helpers.cc b/quic/quartc/quartc_crypto_helpers.cc
new file mode 100644
index 0000000..865fec4
--- /dev/null
+++ b/quic/quartc/quartc_crypto_helpers.cc
@@ -0,0 +1,162 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+
+namespace quic {
+
+void DummyProofSource::GetProof(const QuicSocketAddress& server_address,
+                                const QuicString& hostname,
+                                const QuicString& server_config,
+                                QuicTransportVersion transport_version,
+                                QuicStringPiece chlo_hash,
+                                std::unique_ptr<Callback> callback) {
+  QuicReferenceCountedPointer<ProofSource::Chain> chain =
+      GetCertChain(server_address, hostname);
+  QuicCryptoProof proof;
+  proof.signature = "Dummy signature";
+  proof.leaf_cert_scts = "Dummy timestamp";
+  callback->Run(true, chain, proof, nullptr /* details */);
+}
+
+QuicReferenceCountedPointer<DummyProofSource::Chain>
+DummyProofSource::GetCertChain(const QuicSocketAddress& server_address,
+                               const QuicString& hostname) {
+  std::vector<QuicString> certs;
+  certs.push_back(kDummyCertName);
+  return QuicReferenceCountedPointer<ProofSource::Chain>(
+      new ProofSource::Chain(certs));
+}
+
+void DummyProofSource::ComputeTlsSignature(
+    const QuicSocketAddress& server_address,
+    const QuicString& hostname,
+    uint16_t signature_algorithm,
+    QuicStringPiece in,
+    std::unique_ptr<SignatureCallback> callback) {
+  callback->Run(true, "Dummy signature");
+}
+
+QuicAsyncStatus InsecureProofVerifier::VerifyProof(
+    const QuicString& hostname,
+    const uint16_t port,
+    const QuicString& server_config,
+    QuicTransportVersion transport_version,
+    QuicStringPiece chlo_hash,
+    const std::vector<QuicString>& certs,
+    const QuicString& cert_sct,
+    const QuicString& signature,
+    const ProofVerifyContext* context,
+    QuicString* error_details,
+    std::unique_ptr<ProofVerifyDetails>* verify_details,
+    std::unique_ptr<ProofVerifierCallback> callback) {
+  return QUIC_SUCCESS;
+}
+
+QuicAsyncStatus InsecureProofVerifier::VerifyCertChain(
+    const QuicString& hostname,
+    const std::vector<QuicString>& certs,
+    const ProofVerifyContext* context,
+    QuicString* error_details,
+    std::unique_ptr<ProofVerifyDetails>* details,
+    std::unique_ptr<ProofVerifierCallback> callback) {
+  return QUIC_SUCCESS;
+}
+
+std::unique_ptr<ProofVerifyContext>
+InsecureProofVerifier::CreateDefaultContext() {
+  return nullptr;
+}
+
+QuicConnectionId QuartcCryptoServerStreamHelper::GenerateConnectionIdForReject(
+    QuicTransportVersion version,
+    QuicConnectionId connection_id) const {
+  // TODO(b/124399417):  Request a zero-length connection id here when the QUIC
+  // server perspective supports it.  Right now, the stateless rejector requires
+  // a connection id that is not the same as the client-chosen connection id.
+  return QuicUtils::CreateRandomConnectionId();
+}
+
+bool QuartcCryptoServerStreamHelper::CanAcceptClientHello(
+    const CryptoHandshakeMessage& message,
+    const QuicSocketAddress& client_address,
+    const QuicSocketAddress& peer_address,
+    const QuicSocketAddress& self_address,
+    QuicString* error_details) const {
+  return true;
+}
+
+std::unique_ptr<QuicCryptoClientConfig> CreateCryptoClientConfig(
+    QuicStringPiece pre_shared_key) {
+  auto config = QuicMakeUnique<QuicCryptoClientConfig>(
+      QuicMakeUnique<InsecureProofVerifier>(),
+      TlsClientHandshaker::CreateSslCtx());
+  config->set_pad_inchoate_hello(false);
+  config->set_pad_full_hello(false);
+  if (!pre_shared_key.empty()) {
+    config->set_pre_shared_key(pre_shared_key);
+  }
+  return config;
+}
+
+CryptoServerConfig CreateCryptoServerConfig(QuicRandom* random,
+                                            const QuicClock* clock,
+                                            QuicStringPiece pre_shared_key) {
+  CryptoServerConfig crypto_server_config;
+
+  // Generate a random source address token secret. For long-running servers
+  // it's better to not regenerate it for each connection to enable zero-RTT
+  // handshakes, but for transient clients it does not matter.
+  char source_address_token_secret[kInputKeyingMaterialLength];
+  random->RandBytes(source_address_token_secret, kInputKeyingMaterialLength);
+  auto config = QuicMakeUnique<QuicCryptoServerConfig>(
+      QuicString(source_address_token_secret, kInputKeyingMaterialLength),
+      random, QuicMakeUnique<DummyProofSource>(), KeyExchangeSource::Default(),
+      TlsServerHandshaker::CreateSslCtx());
+
+  // We run QUIC over ICE, and ICE is verifying remote side with STUN pings.
+  // We disable source address token validation in order to allow for 0-rtt
+  // setup (plus source ip addresses are changing even during the connection
+  // when ICE is used).
+  config->set_validate_source_address_token(false);
+
+  // Effectively disables the anti-amplification measures (we don't need
+  // them because we use ICE, and we need to disable them because we disable
+  // padding of crypto packets).
+  // This multiplier must be large enough so that the crypto handshake packet
+  // (approx. 300 bytes) multiplied by this multiplier is larger than a fully
+  // sized packet (currently 1200 bytes).
+  // 1500 is a bit extreme: if you can imagine sending a 1 byte packet, and
+  // your largest MTU would be below 1500 bytes, 1500*1 >=
+  // any_packet_that_you_can_imagine_sending.
+  // (again, we hardcode packet size to 1200, so we are not dealing with jumbo
+  // frames).
+  config->set_chlo_multiplier(1500);
+
+  // We are sending small client hello, we must not validate its size.
+  config->set_validate_chlo_size(false);
+
+  // Provide server with serialized config string to prove ownership.
+  QuicCryptoServerConfig::ConfigOptions options;
+  // The |message| is used to handle the return value of AddDefaultConfig
+  // which is raw pointer of the CryptoHandshakeMessage.
+  std::unique_ptr<CryptoHandshakeMessage> message(
+      config->AddDefaultConfig(random, clock, options));
+  config->set_pad_rej(false);
+  config->set_pad_shlo(false);
+  if (!pre_shared_key.empty()) {
+    config->set_pre_shared_key(pre_shared_key);
+  }
+  crypto_server_config.config = std::move(config);
+  const QuicData& data = message->GetSerialized();
+
+  crypto_server_config.serialized_crypto_config =
+      QuicString(data.data(), data.length());
+  return crypto_server_config;
+}
+
+}  // namespace quic
diff --git a/quic/quartc/quartc_crypto_helpers.h b/quic/quartc/quartc_crypto_helpers.h
new file mode 100644
index 0000000..2b280b5
--- /dev/null
+++ b/quic/quartc/quartc_crypto_helpers.h
@@ -0,0 +1,121 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_QUARTC_QUARTC_CRYPTO_HELPERS_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_CRYPTO_HELPERS_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h"
+#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// Never, ever, change this certificate name. You will break 0-rtt handshake if
+// you do.
+static constexpr char kDummyCertName[] = "Dummy cert";
+
+struct CryptoServerConfig {
+  std::unique_ptr<QuicCryptoServerConfig> config;
+  QuicString serialized_crypto_config;
+};
+
+// Length of HKDF input keying material, equal to its number of bytes.
+// https://tools.ietf.org/html/rfc5869#section-2.2.
+// TODO(zhihuang): Verify that input keying material length is correct.
+constexpr size_t kInputKeyingMaterialLength = 32;
+
+// Used by QuicCryptoServerConfig to provide dummy proof credentials.
+// TODO(zhihuang): Remove when secure P2P QUIC handshake is possible.
+class DummyProofSource : public ProofSource {
+ public:
+  DummyProofSource() {}
+  ~DummyProofSource() override {}
+
+  // ProofSource overrides.
+  void GetProof(const QuicSocketAddress& server_address,
+                const QuicString& hostname,
+                const QuicString& server_config,
+                QuicTransportVersion transport_version,
+                QuicStringPiece chlo_hash,
+                std::unique_ptr<Callback> callback) override;
+
+  QuicReferenceCountedPointer<Chain> GetCertChain(
+      const QuicSocketAddress& server_address,
+      const QuicString& hostname) override;
+
+  void ComputeTlsSignature(
+      const QuicSocketAddress& server_address,
+      const QuicString& hostname,
+      uint16_t signature_algorithm,
+      QuicStringPiece in,
+      std::unique_ptr<SignatureCallback> callback) override;
+};
+
+// Used by QuicCryptoClientConfig to ignore the peer's credentials
+// and establish an insecure QUIC connection.
+// TODO(zhihuang): Remove when secure P2P QUIC handshake is possible.
+class InsecureProofVerifier : public ProofVerifier {
+ public:
+  InsecureProofVerifier() {}
+  ~InsecureProofVerifier() override {}
+
+  // ProofVerifier overrides.
+  QuicAsyncStatus VerifyProof(
+      const QuicString& hostname,
+      const uint16_t port,
+      const QuicString& server_config,
+      QuicTransportVersion transport_version,
+      QuicStringPiece chlo_hash,
+      const std::vector<QuicString>& certs,
+      const QuicString& cert_sct,
+      const QuicString& signature,
+      const ProofVerifyContext* context,
+      QuicString* error_details,
+      std::unique_ptr<ProofVerifyDetails>* verify_details,
+      std::unique_ptr<ProofVerifierCallback> callback) override;
+
+  QuicAsyncStatus VerifyCertChain(
+      const QuicString& hostname,
+      const std::vector<QuicString>& certs,
+      const ProofVerifyContext* context,
+      QuicString* error_details,
+      std::unique_ptr<ProofVerifyDetails>* details,
+      std::unique_ptr<ProofVerifierCallback> callback) override;
+
+  std::unique_ptr<ProofVerifyContext> CreateDefaultContext() override;
+};
+
+// Implementation of the server-side crypto stream helper.
+class QuartcCryptoServerStreamHelper : public QuicCryptoServerStream::Helper {
+ public:
+  QuicConnectionId GenerateConnectionIdForReject(
+      QuicTransportVersion version,
+      QuicConnectionId connection_id) const override;
+
+  bool CanAcceptClientHello(const CryptoHandshakeMessage& message,
+                            const QuicSocketAddress& client_address,
+                            const QuicSocketAddress& peer_address,
+                            const QuicSocketAddress& self_address,
+                            QuicString* error_details) const override;
+};
+
+std::unique_ptr<QuicCryptoClientConfig> CreateCryptoClientConfig(
+    QuicStringPiece pre_shared_key);
+
+CryptoServerConfig CreateCryptoServerConfig(QuicRandom* random,
+                                            const QuicClock* clock,
+                                            QuicStringPiece pre_shared_key);
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_QUARTC_QUARTC_CRYPTO_HELPERS_H_
diff --git a/quic/quartc/quartc_dispatcher.cc b/quic/quartc/quartc_dispatcher.cc
new file mode 100644
index 0000000..7c398e2
--- /dev/null
+++ b/quic/quartc/quartc_dispatcher.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_factory.h"
+
+namespace quic {
+
+QuartcDispatcher::QuartcDispatcher(
+    std::unique_ptr<QuicConfig> config,
+    std::unique_ptr<QuicCryptoServerConfig> crypto_config,
+    QuicStringPiece crypto_config_serialized,
+    std::unique_ptr<QuicVersionManager> version_manager,
+    std::unique_ptr<QuicConnectionHelperInterface> helper,
+    std::unique_ptr<QuicCryptoServerStream::Helper> session_helper,
+    std::unique_ptr<QuicAlarmFactory> alarm_factory,
+    std::unique_ptr<QuartcPacketWriter> packet_writer,
+    Delegate* delegate)
+    : QuicDispatcher(config.get(),
+                     crypto_config.get(),
+                     version_manager.get(),
+                     std::move(helper),
+                     std::move(session_helper),
+                     std::move(alarm_factory)),
+      owned_quic_config_(std::move(config)),
+      owned_crypto_config_(std::move(crypto_config)),
+      crypto_config_(crypto_config_serialized),
+      owned_version_manager_(std::move(version_manager)),
+      delegate_(delegate),
+      packet_writer_(packet_writer.get()) {
+  // QuicDispatcher takes ownership of the writer.
+  QuicDispatcher::InitializeWithWriter(packet_writer.release());
+  // NB: This must happen *after* InitializeWithWriter.  It can call us back
+  // with OnTransportCanWrite() immediately, and the dispatcher needs to be
+  // fully initialized to handle that.
+  packet_writer_->SetPacketTransportDelegate(this);
+}
+
+QuartcDispatcher::~QuartcDispatcher() {
+  packet_writer_->SetPacketTransportDelegate(nullptr);
+}
+
+QuartcSession* QuartcDispatcher::CreateQuicSession(
+    QuicConnectionId connection_id,
+    const QuicSocketAddress& client_address,
+    QuicStringPiece alpn,
+    const ParsedQuicVersion& version) {
+  std::unique_ptr<QuicConnection> connection = CreateQuicConnection(
+      connection_id, client_address, helper(), alarm_factory(), writer(),
+      Perspective::IS_SERVER, ParsedQuicVersionVector{version});
+  QuartcSession* session = new QuartcServerSession(
+      std::move(connection), /*visitor=*/this, config(), GetSupportedVersions(),
+      helper()->GetClock(), crypto_config(), compressed_certs_cache(),
+      session_helper());
+  delegate_->OnSessionCreated(session);
+  return session;
+}
+
+void QuartcDispatcher::OnTransportCanWrite() {
+  OnCanWrite();
+}
+
+void QuartcDispatcher::OnTransportReceived(const char* data, size_t data_len) {
+  // QuartcPacketTransport does not surface real peer addresses, so the
+  // dispatcher uses a dummy address when processing incoming packets. Note that
+  // the dispatcher refuses to process anything with port 0.
+  static const QuicSocketAddress* dummy_address =
+      new QuicSocketAddress(QuicIpAddress::Any4(), /*port=*/1);
+
+  QuicReceivedPacket packet(data, data_len, helper()->GetClock()->Now());
+  ProcessPacket(/*self_address=*/*dummy_address,
+                /*peer_address=*/*dummy_address, packet);
+}
+
+}  // namespace quic
diff --git a/quic/quartc/quartc_dispatcher.h b/quic/quartc/quartc_dispatcher.h
new file mode 100644
index 0000000..6022720
--- /dev/null
+++ b/quic/quartc/quartc_dispatcher.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_QUARTC_QUARTC_DISPATCHER_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_DISPATCHER_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
+#include "net/third_party/quiche/src/quic/core/quic_version_manager.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_session.h"
+
+namespace quic {
+
+class QuartcDispatcher : public QuicDispatcher,
+                         QuartcPacketTransport::Delegate {
+ public:
+  class Delegate {
+   public:
+    virtual ~Delegate() = default;
+    virtual void OnSessionCreated(QuartcSession* session) = 0;
+  };
+
+  QuartcDispatcher(
+      std::unique_ptr<QuicConfig> config,
+      std::unique_ptr<QuicCryptoServerConfig> crypto_config,
+      QuicStringPiece crypto_config_serialized,
+      std::unique_ptr<QuicVersionManager> version_manager,
+      std::unique_ptr<QuicConnectionHelperInterface> helper,
+      std::unique_ptr<QuicCryptoServerStream::Helper> session_helper,
+      std::unique_ptr<QuicAlarmFactory> alarm_factory,
+      std::unique_ptr<QuartcPacketWriter> packet_writer,
+      Delegate* delegate);
+  ~QuartcDispatcher() override;
+
+  QuartcSession* CreateQuicSession(QuicConnectionId connection_id,
+                                   const QuicSocketAddress& client_address,
+                                   QuicStringPiece alpn,
+                                   const ParsedQuicVersion& version) override;
+
+  // QuartcPacketTransport::Delegate overrides.
+  void OnTransportCanWrite() override;
+  void OnTransportReceived(const char* data, size_t data_len) override;
+
+  // A serialized server config in quic wire format.
+  QuicStringPiece server_crypto_config() const { return crypto_config_; }
+
+ private:
+  // Members owned by QuartcDispatcher but not QuicDispatcher.
+  std::unique_ptr<QuicConfig> owned_quic_config_;
+  std::unique_ptr<QuicCryptoServerConfig> owned_crypto_config_;
+  QuicString crypto_config_;
+  std::unique_ptr<QuicVersionManager> owned_version_manager_;
+
+  // Delegate invoked when the dispatcher creates a new session.
+  Delegate* delegate_;
+
+  // The packet writer used by this dispatcher.  Owned by the base class, but
+  // the base class upcasts it to QuicPacketWriter (which prevents detaching the
+  // transport delegate without a downcast).
+  QuartcPacketWriter* packet_writer_;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_QUARTC_QUARTC_DISPATCHER_H_
diff --git a/quic/quartc/quartc_endpoint.cc b/quic/quartc/quartc_endpoint.cc
new file mode 100644
index 0000000..8d03b0a
--- /dev/null
+++ b/quic/quartc/quartc_endpoint.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/quartc/quartc_endpoint.h"
+#include "net/third_party/quiche/src/quic/core/quic_version_manager.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h"
+
+namespace quic {
+
+namespace {
+
+// Wrapper around a QuicAlarmFactory which delegates to the wrapped factory.
+// Usee to convert an unowned pointer into an owned pointer, so that the new
+// "owner" does not delete the underlying factory.  Note that this is only valid
+// when the unowned pointer is already guaranteed to outlive the new "owner".
+class QuartcAlarmFactoryWrapper : public QuicAlarmFactory {
+ public:
+  explicit QuartcAlarmFactoryWrapper(QuicAlarmFactory* impl) : impl_(impl) {}
+
+  QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) override;
+  QuicArenaScopedPtr<QuicAlarm> CreateAlarm(
+      QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
+      QuicConnectionArena* arena) override;
+
+ private:
+  QuicAlarmFactory* impl_;
+};
+
+QuicAlarm* QuartcAlarmFactoryWrapper::CreateAlarm(
+    QuicAlarm::Delegate* delegate) {
+  return impl_->CreateAlarm(delegate);
+}
+
+QuicArenaScopedPtr<QuicAlarm> QuartcAlarmFactoryWrapper::CreateAlarm(
+    QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
+    QuicConnectionArena* arena) {
+  return impl_->CreateAlarm(std::move(delegate), arena);
+}
+
+QuartcFactoryConfig CreateFactoryConfig(QuicAlarmFactory* alarm_factory,
+                                        const QuicClock* clock) {
+  QuartcFactoryConfig config;
+  config.alarm_factory = alarm_factory;
+  config.clock = clock;
+  return config;
+}
+
+}  // namespace
+
+QuartcClientEndpoint::QuartcClientEndpoint(
+    QuicAlarmFactory* alarm_factory,
+    const QuicClock* clock,
+    QuartcEndpoint::Delegate* delegate,
+    QuicStringPiece serialized_server_config)
+    : alarm_factory_(alarm_factory),
+      clock_(clock),
+      delegate_(delegate),
+      serialized_server_config_(serialized_server_config),
+      create_session_alarm_(QuicWrapUnique(
+          alarm_factory_->CreateAlarm(new CreateSessionDelegate(this)))),
+      factory_(QuicMakeUnique<QuartcFactory>(
+          CreateFactoryConfig(alarm_factory, clock))) {}
+
+void QuartcClientEndpoint::Connect(const QuartcSessionConfig& config) {
+  config_ = config;
+  create_session_alarm_->Set(clock_->Now());
+}
+
+void QuartcClientEndpoint::OnCreateSessionAlarm() {
+  session_ =
+      factory_->CreateQuartcClientSession(config_, serialized_server_config_);
+  delegate_->OnSessionCreated(session_.get());
+}
+
+QuartcServerEndpoint::QuartcServerEndpoint(QuicAlarmFactory* alarm_factory,
+                                           const QuicClock* clock,
+                                           QuartcEndpoint::Delegate* delegate)
+    : alarm_factory_(alarm_factory), clock_(clock), delegate_(delegate) {}
+
+void QuartcServerEndpoint::Connect(const QuartcSessionConfig& config) {
+  auto connection_helper = QuicMakeUnique<QuartcConnectionHelper>(clock_);
+  auto crypto_config = CreateCryptoServerConfig(
+      connection_helper->GetRandomGenerator(), clock_, config.pre_shared_key);
+  dispatcher_ = QuicMakeUnique<QuartcDispatcher>(
+      QuicMakeUnique<QuicConfig>(CreateQuicConfig(config)),
+      std::move(crypto_config.config), crypto_config.serialized_crypto_config,
+      QuicMakeUnique<QuicVersionManager>(AllSupportedVersions()),
+      std::move(connection_helper),
+      QuicMakeUnique<QuartcCryptoServerStreamHelper>(),
+      QuicMakeUnique<QuartcAlarmFactoryWrapper>(alarm_factory_),
+      QuicMakeUnique<QuartcPacketWriter>(config.packet_transport,
+                                         config.max_packet_size),
+      this);
+  // The dispatcher requires at least one call to |ProcessBufferedChlos| to
+  // set the number of connections it is allowed to create.
+  dispatcher_->ProcessBufferedChlos(/*max_connections_to_create=*/1);
+}
+
+void QuartcServerEndpoint::OnSessionCreated(QuartcSession* session) {
+  delegate_->OnSessionCreated(session);
+}
+
+}  // namespace quic
diff --git a/quic/quartc/quartc_endpoint.h b/quic/quartc/quartc_endpoint.h
new file mode 100644
index 0000000..108fe12
--- /dev/null
+++ b/quic/quartc/quartc_endpoint.h
@@ -0,0 +1,156 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_QUARTC_QUARTC_ENDPOINT_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_ENDPOINT_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_factory.h"
+
+namespace quic {
+
+// Private implementation of QuartcEndpoint.  Enables different implementations
+// for client and server endpoints.
+class QuartcEndpointImpl {
+ public:
+  virtual ~QuartcEndpointImpl() = default;
+
+  virtual QuicStringPiece server_crypto_config() const = 0;
+};
+
+// Endpoint (client or server) in a peer-to-peer Quartc connection.
+class QuartcEndpoint {
+ public:
+  class Delegate {
+   public:
+    virtual ~Delegate() = default;
+
+    // Called when an endpoint creates a new session, before any packets are
+    // processed or sent.  The callee should perform any additional
+    // configuration required, such as setting a session delegate, before
+    // returning.  |session| is owned by the endpoint, but remains safe to use
+    // until another call to |OnSessionCreated| occurs, at which point previous
+    // session is destroyed.
+    virtual void OnSessionCreated(QuartcSession* session) = 0;
+
+    // Called if the endpoint fails to establish a session after a call to
+    // Connect.  (The most likely cause is a network idle timeout.)
+    virtual void OnConnectError(QuicErrorCode error,
+                                const QuicString& error_details) = 0;
+  };
+
+  virtual ~QuartcEndpoint() = default;
+
+  // Connects the endpoint using the given session config.  After |Connect| is
+  // called, the endpoint will asynchronously create a session, then call
+  // |Delegate::OnSessionCreated|.
+  virtual void Connect(const QuartcSessionConfig& config) = 0;
+};
+
+// Implementation of QuartcEndpoint which immediately (but asynchronously)
+// creates a session by scheduling a QuicAlarm.  Only suitable for use with the
+// client perspective.
+class QuartcClientEndpoint : public QuartcEndpoint {
+ public:
+  // |alarm_factory|, |clock|, and |delegate| are owned by the caller and must
+  // outlive the endpoint.
+  QuartcClientEndpoint(QuicAlarmFactory* alarm_factory,
+                       const QuicClock* clock,
+                       Delegate* delegate,
+                       QuicStringPiece serialized_server_config);
+
+  void Connect(const QuartcSessionConfig& config) override;
+
+ private:
+  friend class CreateSessionDelegate;
+  class CreateSessionDelegate : public QuicAlarm::Delegate {
+   public:
+    CreateSessionDelegate(QuartcClientEndpoint* endpoint)
+        : endpoint_(endpoint) {}
+
+    void OnAlarm() override { endpoint_->OnCreateSessionAlarm(); }
+
+   private:
+    QuartcClientEndpoint* endpoint_;
+  };
+
+  // Callback which occurs when |create_session_alarm_| fires.
+  void OnCreateSessionAlarm();
+
+  // Implementation of QuicAlarmFactory used by this endpoint.  Unowned.
+  QuicAlarmFactory* alarm_factory_;
+
+  // Implementation of QuicClock used by this endpoint.  Unowned.
+  const QuicClock* clock_;
+
+  // Delegate which receives callbacks for newly created sessions.
+  QuartcEndpoint::Delegate* delegate_;
+
+  // Server config.  If valid, used to perform a 0-RTT connection.
+  const QuicString serialized_server_config_;
+
+  // Alarm for creating sessions asynchronously.  The alarm is set when
+  // Connect() is called.  When it fires, the endpoint creates a session and
+  // calls the delegate.
+  std::unique_ptr<QuicAlarm> create_session_alarm_;
+
+  // QuartcFactory used by this endpoint to create sessions.  This is an
+  // implementation detail of the QuartcEndpoint, and will eventually be
+  // replaced by a dispatcher (for servers) or version-negotiation agent (for
+  // clients).
+  std::unique_ptr<QuartcFactory> factory_;
+
+  // Config to be used for new sessions.
+  QuartcSessionConfig config_;
+
+  // The currently-active session.  Nullptr until |Connect| and
+  // |Delegate::OnSessionCreated| are called.
+  std::unique_ptr<QuartcSession> session_;
+};
+
+// Implementation of QuartcEndpoint which uses a QuartcDispatcher to listen for
+// an incoming CHLO and create a session when one arrives.  Only suitable for
+// use with the server perspective.
+class QuartcServerEndpoint : public QuartcEndpoint,
+                             public QuartcDispatcher::Delegate {
+ public:
+  QuartcServerEndpoint(QuicAlarmFactory* alarm_factory,
+                       const QuicClock* clock,
+                       QuartcEndpoint::Delegate* delegate);
+
+  // Implements QuartcEndpoint.
+  void Connect(const QuartcSessionConfig& config) override;
+
+  // Implements QuartcDispatcher::Delegate.
+  void OnSessionCreated(QuartcSession* session) override;
+
+  // Accessor to retrieve the server crypto config.  May only be called after
+  // Connect().
+  QuicStringPiece server_crypto_config() const {
+    return dispatcher_->server_crypto_config();
+  }
+
+ private:
+  // Implementation of QuicAlarmFactory used by this endpoint.  Unowned.
+  QuicAlarmFactory* alarm_factory_;
+
+  // Implementation of QuicClock used by this endpoint.  Unowned.
+  const QuicClock* clock_;
+
+  // Delegate which receives callbacks for newly created sessions.
+  QuartcEndpoint::Delegate* delegate_;
+
+  // QuartcDispatcher waits for an incoming CHLO, then either rejects it or
+  // creates a session to respond to it.  The dispatcher owns all sessions it
+  // creates.
+  std::unique_ptr<QuartcDispatcher> dispatcher_;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_QUARTC_QUARTC_ENDPOINT_H_
diff --git a/quic/quartc/quartc_endpoint_test.cc b/quic/quartc/quartc_endpoint_test.cc
new file mode 100644
index 0000000..59ca847
--- /dev/null
+++ b/quic/quartc/quartc_endpoint_test.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/quartc/quartc_endpoint.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+
+namespace quic {
+namespace {
+
+static QuicByteCount kDefaultMaxPacketSize = 1200;
+
+class FakeEndpointDelegate : public QuartcEndpoint::Delegate {
+ public:
+  void OnSessionCreated(QuartcSession* session) override {
+    last_session_ = session;
+  }
+
+  void OnConnectError(QuicErrorCode /*error*/,
+                      const QuicString& /*error_details*/) override {}
+
+  QuartcSession* last_session() { return last_session_; }
+
+ private:
+  QuartcSession* last_session_ = nullptr;
+};
+
+class QuartcEndpointTest : public QuicTest {
+ protected:
+  QuartcEndpointTest()
+      : transport_(&simulator_,
+                   "client_transport",
+                   "server_transport",
+                   10 * kDefaultMaxPacketSize) {}
+
+  simulator::Simulator simulator_;
+  simulator::SimulatedQuartcPacketTransport transport_;
+  FakeEndpointDelegate delegate_;
+};
+
+// After calling Connect, the client endpoint must wait for an async callback.
+// The callback occurs after a finite amount of time and produces a session.
+TEST_F(QuartcEndpointTest, ClientCreatesSessionAsynchronously) {
+  QuartcClientEndpoint endpoint_(simulator_.GetAlarmFactory(),
+                                 simulator_.GetClock(), &delegate_,
+                                 /*serialized_server_config=*/"");
+  QuartcSessionConfig config;
+  config.packet_transport = &transport_;
+  config.max_packet_size = kDefaultMaxPacketSize;
+  endpoint_.Connect(config);
+
+  EXPECT_EQ(delegate_.last_session(), nullptr);
+
+  EXPECT_TRUE(simulator_.RunUntil(
+      [this] { return delegate_.last_session() != nullptr; }));
+}
+
+}  // namespace
+}  // namespace quic
diff --git a/quic/quartc/quartc_factory.cc b/quic/quartc/quartc_factory.cc
index ee637e5..336ca14 100644
--- a/quic/quartc/quartc_factory.cc
+++ b/quic/quartc/quartc_factory.cc
@@ -5,40 +5,63 @@
 #include "net/third_party/quiche/src/quic/quartc/quartc_factory.h"
 
 #include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h"
 #include "net/third_party/quiche/src/quic/quartc/quartc_session.h"
 
 namespace quic {
 
 QuartcFactory::QuartcFactory(const QuartcFactoryConfig& factory_config)
     : alarm_factory_(factory_config.alarm_factory),
-      clock_(factory_config.clock) {}
+      clock_(factory_config.clock),
+      connection_helper_(QuicMakeUnique<QuartcConnectionHelper>(clock_)),
+      compressed_certs_cache_(QuicMakeUnique<QuicCompressedCertsCache>(
+          QuicCompressedCertsCache::kQuicCompressedCertsCacheSize)),
+      stream_helper_(QuicMakeUnique<QuartcCryptoServerStreamHelper>()) {}
 
-QuartcFactory::~QuartcFactory() {}
-
-std::unique_ptr<QuartcSession> QuartcFactory::CreateQuartcSession(
-    const QuartcSessionConfig& quartc_session_config) {
+std::unique_ptr<QuartcSession> QuartcFactory::CreateQuartcClientSession(
+    const QuartcSessionConfig& quartc_session_config,
+    QuicStringPiece server_crypto_config) {
   DCHECK(quartc_session_config.packet_transport);
 
-  Perspective perspective = quartc_session_config.perspective;
-
   // QuartcSession will eventually own both |writer| and |quic_connection|.
   auto writer =
       QuicMakeUnique<QuartcPacketWriter>(quartc_session_config.packet_transport,
                                          quartc_session_config.max_packet_size);
 
+  // While the QuicConfig is not directly used by the connection, creating it
+  // also sets flag values which must be set before creating the connection.
+  QuicConfig quic_config = CreateQuicConfig(quartc_session_config);
+  std::unique_ptr<QuicConnection> quic_connection =
+      CreateQuicConnection(Perspective::IS_CLIENT, writer.get());
+
+  return QuicMakeUnique<QuartcClientSession>(
+      std::move(quic_connection), quic_config, CurrentSupportedVersions(),
+      clock_, std::move(writer),
+      CreateCryptoClientConfig(quartc_session_config.pre_shared_key),
+      server_crypto_config);
+}
+
+QuicConfig CreateQuicConfig(const QuartcSessionConfig& quartc_session_config) {
+  // TODO(b/124398962): Figure out a better way to initialize QUIC flags.
+  // Creating a config shouldn't have global side-effects on flags.  However,
+  // this has the advantage of ensuring that flag values stay in sync with the
+  // options requested by configs, so simply splitting the config and flag
+  // settings doesn't seem preferable.
+
   // Fixes behavior of StopReading() with level-triggered stream sequencers.
   SetQuicReloadableFlag(quic_stop_reading_when_level_triggered, true);
 
   // Fix b/110259444.
   SetQuicReloadableFlag(quic_fix_spurious_ack_alarm, true);
 
-  // Enable version 45+ to enable SendMessage API.
-  // Enable version 47+ to enable 'quic bit' per draft 17.
-  SetQuicReloadableFlag(quic_enable_version_45, true);
+  // Enable version 46+ to enable SendMessage API and 'quic bit' per draft 17.
   SetQuicReloadableFlag(quic_enable_version_46, true);
-  SetQuicReloadableFlag(quic_enable_version_47, true);
 
   // Fix for inconsistent reporting of crypto handshake.
   SetQuicReloadableFlag(quic_fix_has_pending_crypto_data, true);
@@ -57,9 +80,6 @@
   // to implement negotiation outside of QuicConnection.
   SetQuicRestartFlag(quic_no_server_conn_ver_negotiation2, false);
 
-  std::unique_ptr<QuicConnection> quic_connection =
-      CreateQuicConnection(perspective, writer.get());
-
   QuicTagVector copt;
   copt.push_back(kNSTP);
 
@@ -78,22 +98,6 @@
   // Enable time-based loss detection.
   copt.push_back(kTIME);
 
-  QuicSentPacketManager& sent_packet_manager =
-      quic_connection->sent_packet_manager();
-
-  // Default delayed ack time is 25ms.
-  // If data packets are sent less often (e.g. because p-time was modified),
-  // we would force acks to be sent every 25ms regardless, increasing
-  // overhead. Since generally we guarantee a packet every 20ms, changing
-  // this value should have miniscule effect on quality on good connections,
-  // but on poor connections, changing this number significantly reduced the
-  // number of ack-only packets.
-  // The p-time can go up to as high as 120ms, and when it does, it's
-  // when the low overhead is the most important thing. Ideally it should be
-  // above 120ms, but it cannot be higher than 0.5*RTO, which equals to 100ms.
-  sent_packet_manager.set_delayed_ack_time(
-      QuicTime::Delta::FromMilliseconds(100));
-
   // Note: flag settings have no effect for Exoblaze builds since
   // SetQuicReloadableFlag() gets stubbed out.
   SetQuicReloadableFlag(quic_bbr_less_probe_rtt, true);   // Enable BBR6,7,8.
@@ -115,15 +119,6 @@
     copt.push_back(kNTLP);
   }
 
-  quic_connection->set_fill_up_link_during_probing(true);
-
-  // We start ack decimation after 15 packets. Typically, we would see
-  // 1-2 crypto handshake packets, one media packet, and 10 probing packets.
-  // We want to get acks for the probing packets as soon as possible,
-  // but we can start using ack decimation right after first probing completes.
-  // The default was to not start ack decimation for the first 100 packets.
-  quic_connection->set_min_received_before_ack_decimation(15);
-
   // TODO(b/112192153):  Test and possible enable slower startup when pipe
   // filling is ready to use.  Slower startup is kBBRS.
 
@@ -175,42 +170,63 @@
   // number of open streams gives sufficient headroom to recover before QUIC
   // refuses new streams.
   quic_config.SetMaxIncomingDynamicStreamsToSend(1000);
-  return QuicMakeUnique<QuartcSession>(
-      std::move(quic_connection), quic_config, CurrentSupportedVersions(),
-      quartc_session_config.unique_remote_server_id, perspective,
-      this /*QuicConnectionHelperInterface*/, clock_, std::move(writer));
+
+  return quic_config;
 }
 
 std::unique_ptr<QuicConnection> QuartcFactory::CreateQuicConnection(
     Perspective perspective,
     QuartcPacketWriter* packet_writer) {
-  // dummy_id and dummy_address are used because Quartc network layer will not
-  // use these two.
-  QuicConnectionId dummy_id;
-  if (!QuicConnectionIdSupportsVariableLength(perspective)) {
-    dummy_id = QuicConnectionIdFromUInt64(0);
-  } else {
+  // |dummy_id| and |dummy_address| are used because Quartc network layer will
+  // not use these two.
     char connection_id_bytes[sizeof(uint64_t)] = {};
-    dummy_id = QuicConnectionId(static_cast<char*>(connection_id_bytes),
-                                sizeof(connection_id_bytes));
-  }
-  QuicSocketAddress dummy_address(QuicIpAddress::Any4(), 0 /*Port*/);
-  return QuicMakeUnique<QuicConnection>(
-      dummy_id, dummy_address, this, /*QuicConnectionHelperInterface*/
-      alarm_factory_ /*QuicAlarmFactory*/, packet_writer, /*owns_writer=*/false,
-      perspective, CurrentSupportedVersions());
+    QuicConnectionId dummy_id = QuicConnectionId(
+        static_cast<char*>(connection_id_bytes), sizeof(connection_id_bytes));
+    QuicSocketAddress dummy_address(QuicIpAddress::Any4(), /*port=*/0);
+    return quic::CreateQuicConnection(
+        dummy_id, dummy_address, connection_helper_.get(), alarm_factory_,
+        packet_writer, perspective, CurrentSupportedVersions());
 }
 
-const QuicClock* QuartcFactory::GetClock() const {
-  return clock_;
-}
+std::unique_ptr<QuicConnection> CreateQuicConnection(
+    QuicConnectionId connection_id,
+    const QuicSocketAddress& peer_address,
+    QuicConnectionHelperInterface* connection_helper,
+    QuicAlarmFactory* alarm_factory,
+    QuicPacketWriter* packet_writer,
+    Perspective perspective,
+    ParsedQuicVersionVector supported_versions) {
+  auto quic_connection = QuicMakeUnique<QuicConnection>(
+      connection_id, peer_address, connection_helper, alarm_factory,
+      packet_writer,
+      /*owns_writer=*/false, perspective, supported_versions);
 
-QuicRandom* QuartcFactory::GetRandomGenerator() {
-  return QuicRandom::GetInstance();
-}
+  QuicSentPacketManager& sent_packet_manager =
+      quic_connection->sent_packet_manager();
 
-QuicBufferAllocator* QuartcFactory::GetStreamSendBufferAllocator() {
-  return &buffer_allocator_;
+  // Default delayed ack time is 25ms.
+  // If data packets are sent less often (e.g. because p-time was modified),
+  // we would force acks to be sent every 25ms regardless, increasing
+  // overhead. Since generally we guarantee a packet every 20ms, changing
+  // this value should have miniscule effect on quality on good connections,
+  // but on poor connections, changing this number significantly reduced the
+  // number of ack-only packets.
+  // The p-time can go up to as high as 120ms, and when it does, it's
+  // when the low overhead is the most important thing. Ideally it should be
+  // above 120ms, but it cannot be higher than 0.5*RTO, which equals to 100ms.
+  sent_packet_manager.set_delayed_ack_time(
+      QuicTime::Delta::FromMilliseconds(100));
+
+  quic_connection->set_fill_up_link_during_probing(true);
+
+  // We start ack decimation after 15 packets. Typically, we would see
+  // 1-2 crypto handshake packets, one media packet, and 10 probing packets.
+  // We want to get acks for the probing packets as soon as possible,
+  // but we can start using ack decimation right after first probing completes.
+  // The default was to not start ack decimation for the first 100 packets.
+  quic_connection->set_min_received_before_ack_decimation(15);
+
+  return quic_connection;
 }
 
 std::unique_ptr<QuartcFactory> CreateQuartcFactory(
diff --git a/quic/quartc/quartc_factory.h b/quic/quartc/quartc_factory.h
index c4ea67f..ec9fd7b 100644
--- a/quic/quartc/quartc_factory.h
+++ b/quic/quartc/quartc_factory.h
@@ -26,20 +26,18 @@
 };
 
 struct QuartcSessionConfig {
-  // When using Quartc, there are two endpoints. The QuartcSession on one
-  // endpoint must act as a server and the one on the other side must act as a
-  // client.
-  Perspective perspective = Perspective::IS_CLIENT;
-  // This is only needed when is_server = false.  It must be unique
-  // for each endpoint the local endpoint may communicate with. For example,
-  // a WebRTC client could use the remote endpoint's crypto fingerprint
-  QuicString unique_remote_server_id;
+  // If a pre-shared cryptographic key is available for this session, specify it
+  // here.  This value will only be used if non-empty.
+  QuicString pre_shared_key;
+
   // The way the QuicConnection will send and receive packets, like a virtual
   // UDP socket. For WebRTC, this will typically be an IceTransport.
   QuartcPacketTransport* packet_transport = nullptr;
+
   // The maximum size of the packet can be written with the packet writer.
   // 1200 bytes by default.
   QuicPacketLength max_packet_size = 1200;
+
   // Timeouts for the crypto handshake. Set them to higher values to
   // prevent closing the session before it started on a slow network.
   // Zero entries are ignored and QUIC defaults are used in that case.
@@ -57,21 +55,14 @@
 // Factory that creates instances of QuartcSession.  Implements the
 // QuicConnectionHelperInterface used by the QuicConnections. Only one
 // QuartcFactory is expected to be created.
-class QUIC_EXPORT_PRIVATE QuartcFactory : public QuicConnectionHelperInterface {
+class QUIC_EXPORT_PRIVATE QuartcFactory {
  public:
   explicit QuartcFactory(const QuartcFactoryConfig& factory_config);
-  ~QuartcFactory() override;
 
   // Creates a new QuartcSession using the given configuration.
-  std::unique_ptr<QuartcSession> CreateQuartcSession(
-      const QuartcSessionConfig& quartc_session_config);
-
-  // QuicConnectionHelperInterface overrides.
-  const QuicClock* GetClock() const override;
-
-  QuicRandom* GetRandomGenerator() override;
-
-  QuicBufferAllocator* GetStreamSendBufferAllocator() override;
+  std::unique_ptr<QuartcSession> CreateQuartcClientSession(
+      const QuartcSessionConfig& quartc_session_config,
+      QuicStringPiece server_crypto_config);
 
  private:
   std::unique_ptr<QuicConnection> CreateQuicConnection(
@@ -84,9 +75,28 @@
   // Used to implement the QuicConnectionHelperInterface.  Owned by the user and
   // must outlive QuartcFactory.
   const QuicClock* clock_;
-  SimpleBufferAllocator buffer_allocator_;
+
+  // Helper used by all QuicConnections.
+  std::unique_ptr<QuicConnectionHelperInterface> connection_helper_;
+
+  // Used by QuicCryptoServerStream to track most recently compressed certs.
+  std::unique_ptr<QuicCompressedCertsCache> compressed_certs_cache_;
+
+  // This helper is needed to create QuicCryptoServerStreams.
+  std::unique_ptr<QuicCryptoServerStream::Helper> stream_helper_;
 };
 
+QuicConfig CreateQuicConfig(const QuartcSessionConfig& quartc_session_config);
+
+std::unique_ptr<QuicConnection> CreateQuicConnection(
+    QuicConnectionId connection_id,
+    const QuicSocketAddress& peer_address,
+    QuicConnectionHelperInterface* connection_helper,
+    QuicAlarmFactory* alarm_factory,
+    QuicPacketWriter* packet_writer,
+    Perspective perspective,
+    ParsedQuicVersionVector supported_versions);
+
 // Creates a new instance of QuartcFactory.
 std::unique_ptr<QuartcFactory> CreateQuartcFactory(
     const QuartcFactoryConfig& factory_config);
diff --git a/quic/quartc/quartc_interval_counter.h b/quic/quartc/quartc_interval_counter.h
index a9b827c..fe3b083 100644
--- a/quic/quartc/quartc_interval_counter.h
+++ b/quic/quartc/quartc_interval_counter.h
@@ -8,8 +8,8 @@
 #include <stddef.h>
 #include <vector>
 
-#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
-#include "net/third_party/quiche/src/quic/platform/api/quic_interval.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval_set.h"
 
 namespace quic {
 
diff --git a/quic/quartc/quartc_interval_counter_test.cc b/quic/quartc/quartc_interval_counter_test.cc
index 9ba14b2..9bc9af5 100644
--- a/quic/quartc/quartc_interval_counter_test.cc
+++ b/quic/quartc/quartc_interval_counter_test.cc
@@ -6,7 +6,7 @@
 
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "net/third_party/quiche/src/quic/platform/api/quic_interval.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
 
 namespace quic {
diff --git a/quic/quartc/quartc_packet_writer.cc b/quic/quartc/quartc_packet_writer.cc
index 84a8bcf..64a72a8 100644
--- a/quic/quartc/quartc_packet_writer.cc
+++ b/quic/quartc/quartc_packet_writer.cc
@@ -6,6 +6,10 @@
 
 namespace quic {
 
+std::unique_ptr<PerPacketOptions> QuartcPerPacketOptions::Clone() const {
+  return QuicMakeUnique<QuartcPerPacketOptions>(*this);
+}
+
 QuartcPacketWriter::QuartcPacketWriter(QuartcPacketTransport* packet_transport,
                                        QuicByteCount max_packet_size)
     : packet_transport_(packet_transport), max_packet_size_(max_packet_size) {}
@@ -19,10 +23,12 @@
   DCHECK(packet_transport_);
 
   QuartcPacketTransport::PacketInfo info;
-  if (connection_) {
-    info.packet_number = connection_->packet_generator().packet_number();
+  QuartcPerPacketOptions* quartc_options =
+      static_cast<QuartcPerPacketOptions*>(options);
+  if (quartc_options && quartc_options->connection) {
+    info.packet_number =
+        quartc_options->connection->packet_generator().packet_number();
   }
-
   int bytes_written = packet_transport_->Write(buffer, buf_len, info);
   if (bytes_written <= 0) {
     writable_ = false;
@@ -31,10 +37,6 @@
   return WriteResult(WRITE_STATUS_OK, bytes_written);
 }
 
-bool QuartcPacketWriter::IsWriteBlockedDataBuffered() const {
-  return false;
-}
-
 bool QuartcPacketWriter::IsWriteBlocked() const {
   return !writable_;
 }
diff --git a/quic/quartc/quartc_packet_writer.h b/quic/quartc/quartc_packet_writer.h
index 3efb8a8..517d143 100644
--- a/quic/quartc/quartc_packet_writer.h
+++ b/quic/quartc/quartc_packet_writer.h
@@ -51,6 +51,13 @@
   virtual void SetDelegate(Delegate* delegate) = 0;
 };
 
+struct QUIC_EXPORT_PRIVATE QuartcPerPacketOptions : public PerPacketOptions {
+  std::unique_ptr<PerPacketOptions> Clone() const override;
+
+  // The connection which is sending this packet.
+  QuicConnection* connection = nullptr;
+};
+
 // Implements a QuicPacketWriter using a QuartcPacketTransport, which allows a
 // QuicConnection to use (for example), a WebRTC IceTransport.
 class QUIC_EXPORT_PRIVATE QuartcPacketWriter : public QuicPacketWriter {
@@ -67,9 +74,6 @@
                           const QuicSocketAddress& peer_address,
                           PerPacketOptions* options) override;
 
-  // This is always set to false so that QuicConnection buffers unsent packets.
-  bool IsWriteBlockedDataBuffered() const override;
-
   // Whether the underneath |transport_| is blocked. If this returns true,
   // outgoing QUIC packets are queued by QuicConnection until SetWritable() is
   // called.
@@ -93,10 +97,6 @@
 
   WriteResult Flush() override;
 
-  // Sets the connection which sends packets using this writer.  Connection must
-  // be set in order to attach packet info (eg. packet numbers) to writes.
-  void set_connection(QuicConnection* connection) { connection_ = connection; }
-
   void SetPacketTransportDelegate(QuartcPacketTransport::Delegate* delegate);
 
  private:
@@ -105,9 +105,6 @@
   // The maximum size of the packet can be written by this writer.
   QuicByteCount max_packet_size_;
 
-  // The current connection sending packets using this writer.
-  QuicConnection* connection_;
-
   // Whether packets can be written.
   bool writable_ = false;
 };
diff --git a/quic/quartc/quartc_session.cc b/quic/quartc/quartc_session.cc
index 37d2cbe..e169e82 100644
--- a/quic/quartc/quartc_session.cc
+++ b/quic/quartc/quartc_session.cc
@@ -7,202 +7,33 @@
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
 #include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
 #include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h"
 
 namespace quic {
-
 namespace {
 
 // Arbitrary server port number for net::QuicCryptoClientConfig.
 const int kQuicServerPort = 0;
 
-// Length of HKDF input keying material, equal to its number of bytes.
-// https://tools.ietf.org/html/rfc5869#section-2.2.
-// TODO(zhihuang): Verify that input keying material length is correct.
-const size_t kInputKeyingMaterialLength = 32;
-
-// Used by QuicCryptoServerConfig to provide dummy proof credentials.
-// TODO(zhihuang): Remove when secure P2P QUIC handshake is possible.
-class DummyProofSource : public ProofSource {
- public:
-  DummyProofSource() {}
-  ~DummyProofSource() override {}
-
-  // ProofSource override.
-  void GetProof(const QuicSocketAddress& server_address,
-                const QuicString& hostname,
-                const QuicString& server_config,
-                QuicTransportVersion transport_version,
-                QuicStringPiece chlo_hash,
-                std::unique_ptr<Callback> callback) override {
-    QuicReferenceCountedPointer<ProofSource::Chain> chain =
-        GetCertChain(server_address, hostname);
-    QuicCryptoProof proof;
-    proof.signature = "Dummy signature";
-    proof.leaf_cert_scts = "Dummy timestamp";
-    callback->Run(true, chain, proof, nullptr /* details */);
-  }
-
-  QuicReferenceCountedPointer<Chain> GetCertChain(
-      const QuicSocketAddress& server_address,
-      const QuicString& hostname) override {
-    std::vector<QuicString> certs;
-    certs.push_back("Dummy cert");
-    return QuicReferenceCountedPointer<ProofSource::Chain>(
-        new ProofSource::Chain(certs));
-  }
-
-  void ComputeTlsSignature(
-      const QuicSocketAddress& server_address,
-      const QuicString& hostname,
-      uint16_t signature_algorithm,
-      QuicStringPiece in,
-      std::unique_ptr<SignatureCallback> callback) override {
-    callback->Run(true, "Dummy signature");
-  }
-};
-
-// Used by QuicCryptoClientConfig to ignore the peer's credentials
-// and establish an insecure QUIC connection.
-// TODO(zhihuang): Remove when secure P2P QUIC handshake is possible.
-class InsecureProofVerifier : public ProofVerifier {
- public:
-  InsecureProofVerifier() {}
-  ~InsecureProofVerifier() override {}
-
-  // ProofVerifier override.
-  QuicAsyncStatus VerifyProof(
-      const QuicString& hostname,
-      const uint16_t port,
-      const QuicString& server_config,
-      QuicTransportVersion transport_version,
-      QuicStringPiece chlo_hash,
-      const std::vector<QuicString>& certs,
-      const QuicString& cert_sct,
-      const QuicString& signature,
-      const ProofVerifyContext* context,
-      QuicString* error_details,
-      std::unique_ptr<ProofVerifyDetails>* verify_details,
-      std::unique_ptr<ProofVerifierCallback> callback) override {
-    return QUIC_SUCCESS;
-  }
-
-  QuicAsyncStatus VerifyCertChain(
-      const QuicString& hostname,
-      const std::vector<QuicString>& certs,
-      const ProofVerifyContext* context,
-      QuicString* error_details,
-      std::unique_ptr<ProofVerifyDetails>* details,
-      std::unique_ptr<ProofVerifierCallback> callback) override {
-    return QUIC_SUCCESS;
-  }
-
-  std::unique_ptr<ProofVerifyContext> CreateDefaultContext() override {
-    return nullptr;
-  }
-};
-
 }  // namespace
 
-QuicConnectionId QuartcCryptoServerStreamHelper::GenerateConnectionIdForReject(
-    QuicTransportVersion version,
-    QuicConnectionId connection_id) const {
-  return QuicUtils::CreateZeroConnectionId(version);
-}
-
-bool QuartcCryptoServerStreamHelper::CanAcceptClientHello(
-    const CryptoHandshakeMessage& message,
-    const QuicSocketAddress& client_address,
-    const QuicSocketAddress& peer_address,
-    const QuicSocketAddress& self_address,
-    QuicString* error_details) const {
-  return true;
-}
-
 QuartcSession::QuartcSession(std::unique_ptr<QuicConnection> connection,
+                             Visitor* visitor,
                              const QuicConfig& config,
                              const ParsedQuicVersionVector& supported_versions,
-                             const QuicString& unique_remote_server_id,
-                             Perspective perspective,
-                             QuicConnectionHelperInterface* helper,
-                             const QuicClock* clock,
-                             std::unique_ptr<QuartcPacketWriter> packet_writer)
-    : QuicSession(connection.get(),
-                  nullptr /*visitor*/,
-                  config,
-                  supported_versions),
-      unique_remote_server_id_(unique_remote_server_id),
-      perspective_(perspective),
-      packet_writer_(std::move(packet_writer)),
+                             const QuicClock* clock)
+    : QuicSession(connection.get(), visitor, config, supported_versions),
       connection_(std::move(connection)),
-      helper_(helper),
-      clock_(clock) {
-  packet_writer_->set_connection(connection_.get());
-
-  // Initialization with default crypto configuration.
-  if (perspective_ == Perspective::IS_CLIENT) {
-    std::unique_ptr<ProofVerifier> proof_verifier(new InsecureProofVerifier);
-    quic_crypto_client_config_ = QuicMakeUnique<QuicCryptoClientConfig>(
-        std::move(proof_verifier), TlsClientHandshaker::CreateSslCtx());
-    quic_crypto_client_config_->set_pad_inchoate_hello(false);
-    quic_crypto_client_config_->set_pad_full_hello(false);
-  } else {
-    std::unique_ptr<ProofSource> proof_source(new DummyProofSource);
-    // Generate a random source address token secret. For long-running servers
-    // it's better to not regenerate it for each connection to enable zero-RTT
-    // handshakes, but for transient clients it does not matter.
-    char source_address_token_secret[kInputKeyingMaterialLength];
-    helper_->GetRandomGenerator()->RandBytes(source_address_token_secret,
-                                             kInputKeyingMaterialLength);
-    quic_crypto_server_config_ = QuicMakeUnique<QuicCryptoServerConfig>(
-        QuicString(source_address_token_secret, kInputKeyingMaterialLength),
-        helper_->GetRandomGenerator(), std::move(proof_source),
-        KeyExchangeSource::Default(), TlsServerHandshaker::CreateSslCtx());
-
-    // Effectively disables the anti-amplification measures (we don't need
-    // them because we use ICE, and we need to disable them because we disable
-    // padding of crypto packets).
-    // This multiplier must be large enough so that the crypto handshake packet
-    // (approx. 300 bytes) multiplied by this multiplier is larger than a fully
-    // sized packet (currently 1200 bytes).
-    // 1500 is a bit extreme: if you can imagine sending a 1 byte packet, and
-    // your largest MTU would be below 1500 bytes, 1500*1 >=
-    // any_packet_that_you_can_imagine_sending.
-    // (again, we hardcode packet size to 1200, so we are not dealing with jumbo
-    // frames).
-    quic_crypto_server_config_->set_chlo_multiplier(1500);
-
-    // We are sending small client hello, we must not validate its size.
-    quic_crypto_server_config_->set_validate_chlo_size(false);
-
-    // We run QUIC over ICE, and ICE is verifying remote side with STUN pings.
-    // We disable source address token validation in order to allow for 0-rtt
-    // setup (plus source ip addresses are changing even during the connection
-    // when ICE is used).
-    quic_crypto_server_config_->set_validate_source_address_token(false);
-
-    // Provide server with serialized config string to prove ownership.
-    QuicCryptoServerConfig::ConfigOptions options;
-    // The |message| is used to handle the return value of AddDefaultConfig
-    // which is raw pointer of the CryptoHandshakeMessage.
-    std::unique_ptr<CryptoHandshakeMessage> message(
-        quic_crypto_server_config_->AddDefaultConfig(
-            helper_->GetRandomGenerator(), helper_->GetClock(), options));
-    quic_crypto_server_config_->set_pad_rej(false);
-    quic_crypto_server_config_->set_pad_shlo(false);
-  }
+      clock_(clock),
+      per_packet_options_(QuicMakeUnique<QuartcPerPacketOptions>()) {
+  per_packet_options_->connection = connection_.get();
+  connection_->set_per_packet_options(per_packet_options_.get());
 }
 
 QuartcSession::~QuartcSession() {}
 
-const QuicCryptoStream* QuartcSession::GetCryptoStream() const {
-  return crypto_stream_.get();
-}
-
-QuicCryptoStream* QuartcSession::GetMutableCryptoStream() {
-  return crypto_stream_.get();
-}
-
 QuartcStream* QuartcSession::CreateOutgoingBidirectionalStream() {
   // Use default priority for incoming QUIC streams.
   // TODO(zhihuang): Determine if this value is correct.
@@ -234,7 +65,12 @@
 
 void QuartcSession::ProcessSendMessageQueue() {
   while (!send_message_queue_.empty()) {
-    MessageResult result = SendMessage(send_message_queue_.front());
+    struct iovec iov = {const_cast<char*>(send_message_queue_.front().data()),
+                        send_message_queue_.front().length()};
+    QuicMemSliceStorage storage(
+        &iov, 1, connection()->helper()->GetStreamSendBufferAllocator(),
+        send_message_queue_.front().length());
+    MessageResult result = SendMessage(storage.ToSpan());
 
     const size_t message_size = send_message_queue_.front().size();
 
@@ -346,45 +182,6 @@
   QuicSession::OnConnectionClosed(error, error_details, source);
   DCHECK(session_delegate_);
   session_delegate_->OnConnectionClosed(error, error_details, source);
-
-  // The session may be deleted after OnConnectionClosed(), so |this| must be
-  // removed from the packet transport's delegate before it is deleted.
-  packet_writer_->SetPacketTransportDelegate(nullptr);
-}
-
-void QuartcSession::SetPreSharedKey(QuicStringPiece key) {
-  if (perspective_ == Perspective::IS_CLIENT) {
-    quic_crypto_client_config_->set_pre_shared_key(key);
-  } else {
-    quic_crypto_server_config_->set_pre_shared_key(key);
-  }
-}
-
-void QuartcSession::StartCryptoHandshake() {
-  if (perspective_ == Perspective::IS_CLIENT) {
-    QuicServerId server_id(unique_remote_server_id_, kQuicServerPort,
-                           /*privacy_mode_enabled=*/false);
-    QuicCryptoClientStream* crypto_stream = new QuicCryptoClientStream(
-        server_id, this,
-        quic_crypto_client_config_->proof_verifier()->CreateDefaultContext(),
-        quic_crypto_client_config_.get(), this);
-    crypto_stream_.reset(crypto_stream);
-    QuicSession::Initialize();
-    crypto_stream->CryptoConnect();
-  } else {
-    quic_compressed_certs_cache_.reset(new QuicCompressedCertsCache(
-        QuicCompressedCertsCache::kQuicCompressedCertsCacheSize));
-    bool use_stateless_rejects_if_peer_supported = false;
-    QuicCryptoServerStream* crypto_stream = new QuicCryptoServerStream(
-        quic_crypto_server_config_.get(), quic_compressed_certs_cache_.get(),
-        use_stateless_rejects_if_peer_supported, this, &stream_helper_);
-    crypto_stream_.reset(crypto_stream);
-    QuicSession::Initialize();
-  }
-
-  // QUIC is ready to process incoming packets after QuicSession::Initialize().
-  // Set the packet transport delegate to begin receiving packets.
-  packet_writer_->SetPacketTransportDelegate(this);
 }
 
 void QuartcSession::CloseConnection(const QuicString& details) {
@@ -418,16 +215,6 @@
   session_delegate_->OnMessageReceived(message);
 }
 
-void QuartcSession::OnProofValid(
-    const QuicCryptoClientConfig::CachedState& cached) {
-  // TODO(zhihuang): Handle the proof verification.
-}
-
-void QuartcSession::OnProofVerifyDetailsAvailable(
-    const ProofVerifyDetails& verify_details) {
-  // TODO(zhihuang): Handle the proof verification.
-}
-
 QuicStream* QuartcSession::CreateIncomingStream(QuicStreamId id) {
   return ActivateDataStream(CreateDataStream(id, QuicStream::kDefaultPriority));
 }
@@ -440,7 +227,8 @@
 std::unique_ptr<QuartcStream> QuartcSession::CreateDataStream(
     QuicStreamId id,
     spdy::SpdyPriority priority) {
-  if (crypto_stream_ == nullptr || !crypto_stream_->encryption_established()) {
+  if (GetCryptoStream() == nullptr ||
+      !GetCryptoStream()->encryption_established()) {
     // Encryption not active so no stream created
     return nullptr;
   }
@@ -481,4 +269,124 @@
   return raw;
 }
 
+QuartcClientSession::QuartcClientSession(
+    std::unique_ptr<QuicConnection> connection,
+    const QuicConfig& config,
+    const ParsedQuicVersionVector& supported_versions,
+    const QuicClock* clock,
+    std::unique_ptr<QuartcPacketWriter> packet_writer,
+    std::unique_ptr<QuicCryptoClientConfig> client_crypto_config,
+    QuicStringPiece server_crypto_config)
+    : QuartcSession(std::move(connection),
+                    /*visitor=*/nullptr,
+                    config,
+                    supported_versions,
+                    clock),
+      packet_writer_(std::move(packet_writer)),
+      client_crypto_config_(std::move(client_crypto_config)),
+      server_config_(server_crypto_config) {
+  DCHECK_EQ(QuartcSession::connection()->perspective(), Perspective::IS_CLIENT);
+}
+
+QuartcClientSession::~QuartcClientSession() {
+  // The client session is the packet transport delegate, so it must be unset
+  // before the session is deleted.
+  packet_writer_->SetPacketTransportDelegate(nullptr);
+}
+
+void QuartcClientSession::Initialize() {
+  DCHECK(crypto_stream_) << "Do not call QuartcSession::Initialize(), call "
+                            "StartCryptoHandshake() instead.";
+  QuartcSession::Initialize();
+
+  // QUIC is ready to process incoming packets after Initialize().
+  // Set the packet transport delegate to begin receiving packets.
+  packet_writer_->SetPacketTransportDelegate(this);
+}
+
+const QuicCryptoStream* QuartcClientSession::GetCryptoStream() const {
+  return crypto_stream_.get();
+}
+
+QuicCryptoStream* QuartcClientSession::GetMutableCryptoStream() {
+  return crypto_stream_.get();
+}
+
+void QuartcClientSession::StartCryptoHandshake() {
+  QuicServerId server_id(/*host=*/"", kQuicServerPort,
+                         /*privacy_mode_enabled=*/false);
+
+  if (!server_config_.empty()) {
+    QuicCryptoServerConfig::ConfigOptions options;
+
+    QuicString error;
+    QuicWallTime now = clock()->WallNow();
+    QuicCryptoClientConfig::CachedState::ServerConfigState result =
+        client_crypto_config_->LookupOrCreate(server_id)->SetServerConfig(
+            server_config_, now,
+            /*expiry_time=*/now.Add(QuicTime::Delta::Infinite()), &error);
+
+    if (result == QuicCryptoClientConfig::CachedState::SERVER_CONFIG_VALID) {
+      DCHECK_EQ(error, "");
+      client_crypto_config_->LookupOrCreate(server_id)->SetProof(
+          std::vector<QuicString>{kDummyCertName}, /*cert_sct=*/"",
+          /*chlo_hash=*/"", /*signature=*/"anything");
+    } else {
+      LOG(DFATAL) << "Unable to set server config, error=" << error;
+    }
+  }
+
+  crypto_stream_ = QuicMakeUnique<QuicCryptoClientStream>(
+      server_id, this,
+      client_crypto_config_->proof_verifier()->CreateDefaultContext(),
+      client_crypto_config_.get(), this);
+  Initialize();
+  crypto_stream_->CryptoConnect();
+}
+
+void QuartcClientSession::OnProofValid(
+    const QuicCryptoClientConfig::CachedState& cached) {
+  // TODO(zhihuang): Handle the proof verification.
+}
+
+void QuartcClientSession::OnProofVerifyDetailsAvailable(
+    const ProofVerifyDetails& verify_details) {
+  // TODO(zhihuang): Handle the proof verification.
+}
+
+QuartcServerSession::QuartcServerSession(
+    std::unique_ptr<QuicConnection> connection,
+    Visitor* visitor,
+    const QuicConfig& config,
+    const ParsedQuicVersionVector& supported_versions,
+    const QuicClock* clock,
+    const QuicCryptoServerConfig* server_crypto_config,
+    QuicCompressedCertsCache* const compressed_certs_cache,
+    QuicCryptoServerStream::Helper* const stream_helper)
+    : QuartcSession(std::move(connection),
+                    visitor,
+                    config,
+                    supported_versions,
+                    clock),
+      server_crypto_config_(server_crypto_config),
+      compressed_certs_cache_(compressed_certs_cache),
+      stream_helper_(stream_helper) {
+  DCHECK_EQ(QuartcSession::connection()->perspective(), Perspective::IS_SERVER);
+}
+
+const QuicCryptoStream* QuartcServerSession::GetCryptoStream() const {
+  return crypto_stream_.get();
+}
+
+QuicCryptoStream* QuartcServerSession::GetMutableCryptoStream() {
+  return crypto_stream_.get();
+}
+
+void QuartcServerSession::StartCryptoHandshake() {
+  crypto_stream_ = QuicMakeUnique<QuicCryptoServerStream>(
+      server_crypto_config_, compressed_certs_cache_,
+      /*use_stateless_rejects_if_peer_supported=*/false, this, stream_helper_);
+  Initialize();
+}
+
 }  // namespace quic
diff --git a/quic/quartc/quartc_session.h b/quic/quartc/quartc_session.h
index 411c94c..0c414d8 100644
--- a/quic/quartc/quartc_session.h
+++ b/quic/quartc/quartc_session.h
@@ -20,43 +20,21 @@
 
 namespace quic {
 
-// A helper class is used by the QuicCryptoServerStream.
-class QuartcCryptoServerStreamHelper : public QuicCryptoServerStream::Helper {
- public:
-  QuicConnectionId GenerateConnectionIdForReject(
-      QuicTransportVersion version,
-      QuicConnectionId connection_id) const override;
-
-  bool CanAcceptClientHello(const CryptoHandshakeMessage& message,
-                            const QuicSocketAddress& client_address,
-                            const QuicSocketAddress& peer_address,
-                            const QuicSocketAddress& self_address,
-                            QuicString* error_details) const override;
-};
-
 // QuartcSession owns and manages a QUIC connection.
 class QUIC_EXPORT_PRIVATE QuartcSession
     : public QuicSession,
-      public QuartcPacketTransport::Delegate,
-      public QuicCryptoClientStream::ProofHandler {
+      public QuartcPacketTransport::Delegate {
  public:
   QuartcSession(std::unique_ptr<QuicConnection> connection,
+                Visitor* visitor,
                 const QuicConfig& config,
                 const ParsedQuicVersionVector& supported_versions,
-                const QuicString& unique_remote_server_id,
-                Perspective perspective,
-                QuicConnectionHelperInterface* helper,
-                const QuicClock* clock,
-                std::unique_ptr<QuartcPacketWriter> packet_writer);
+                const QuicClock* clock);
   QuartcSession(const QuartcSession&) = delete;
   QuartcSession& operator=(const QuartcSession&) = delete;
   ~QuartcSession() override;
 
   // QuicSession overrides.
-  QuicCryptoStream* GetMutableCryptoStream() override;
-
-  const QuicCryptoStream* GetCryptoStream() const override;
-
   QuartcStream* CreateOutgoingBidirectionalStream();
 
   // Sends short unreliable message using quic message frame (message must fit
@@ -81,7 +59,7 @@
 
   // Return true if transport support message frame.
   bool CanSendMessage() const {
-    return connection()->transport_version() >= QUIC_VERSION_45;
+    return connection()->transport_version() > QUIC_VERSION_44;
   }
 
   void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override;
@@ -96,12 +74,7 @@
                           ConnectionCloseSource source) override;
 
   // QuartcSession methods.
-
-  // Sets a pre-shared key for use during the crypto handshake.  Must be set
-  // before StartCryptoHandshake() is called.
-  void SetPreSharedKey(QuicStringPiece key);
-
-  void StartCryptoHandshake();
+  virtual void StartCryptoHandshake() = 0;
 
   // Closes the connection with the given human-readable error details.
   // The connection closes with the QUIC_CONNECTION_CANCELLED error code to
@@ -171,15 +144,6 @@
 
   void OnMessageReceived(QuicStringPiece message) override;
 
-  // ProofHandler overrides.
-  void OnProofValid(const QuicCryptoClientConfig::CachedState& cached) override;
-
-  // Called by the client crypto handshake when proof verification details
-  // become available, either because proof verification is complete, or when
-  // cached details are used.
-  void OnProofVerifyDetailsAvailable(
-      const ProofVerifyDetails& verify_details) override;
-
   // Returns number of queued (not sent) messages submitted by
   // SendOrQueueMessage. Messages are queued if connection is congestion
   // controlled.
@@ -200,6 +164,8 @@
 
   void ResetStream(QuicStreamId stream_id, QuicRstStreamErrorCode error);
 
+  const QuicClock* clock() { return clock_; }
+
  private:
   std::unique_ptr<QuartcStream> InitializeDataStream(
       std::unique_ptr<QuartcStream> stream,
@@ -207,32 +173,19 @@
 
   void ProcessSendMessageQueue();
 
-  // For crypto handshake.
-  std::unique_ptr<QuicCryptoStream> crypto_stream_;
-  const QuicString unique_remote_server_id_;
-  Perspective perspective_;
-
-  // Packet writer used by |connection_|.
-  std::unique_ptr<QuartcPacketWriter> packet_writer_;
-
   // Take ownership of the QuicConnection.  Note: if |connection_| changes,
   // the new value of |connection_| must be given to |packet_writer_| before any
   // packets are written.  Otherwise, |packet_writer_| will crash.
   std::unique_ptr<QuicConnection> connection_;
-  // Not owned by QuartcSession. From the QuartcFactory.
-  QuicConnectionHelperInterface* helper_;
+
   // For recording packet receipt time
   const QuicClock* clock_;
+
   // Not owned by QuartcSession.
   Delegate* session_delegate_ = nullptr;
-  // Used by QUIC crypto server stream to track most recently compressed certs.
-  std::unique_ptr<QuicCompressedCertsCache> quic_compressed_certs_cache_;
-  // This helper is needed when create QuicCryptoServerStream.
-  QuartcCryptoServerStreamHelper stream_helper_;
-  // Config for QUIC crypto client stream, used by the client.
-  std::unique_ptr<QuicCryptoClientConfig> quic_crypto_client_config_;
-  // Config for QUIC crypto server stream, used by the server.
-  std::unique_ptr<QuicCryptoServerConfig> quic_crypto_server_config_;
+
+  // Options passed to the packet writer for each packet.
+  std::unique_ptr<QuartcPerPacketOptions> per_packet_options_;
 
   // Queue of pending messages sent by SendQuartcMessage that were not sent
   // yet or blocked by congestion control. Messages are queued in the order
@@ -240,6 +193,94 @@
   QuicDeque<QuicString> send_message_queue_;
 };
 
+class QUIC_EXPORT_PRIVATE QuartcClientSession
+    : public QuartcSession,
+      public QuicCryptoClientStream::ProofHandler {
+ public:
+  QuartcClientSession(
+      std::unique_ptr<QuicConnection> connection,
+      const QuicConfig& config,
+      const ParsedQuicVersionVector& supported_versions,
+      const QuicClock* clock,
+      std::unique_ptr<QuartcPacketWriter> packet_writer,
+      std::unique_ptr<QuicCryptoClientConfig> client_crypto_config,
+      QuicStringPiece server_crypto_config);
+  QuartcClientSession(const QuartcClientSession&) = delete;
+  QuartcClientSession& operator=(const QuartcClientSession&) = delete;
+
+  ~QuartcClientSession() override;
+
+  // Initialize should not be called on a QuartcSession.  Instead, call
+  // StartCryptoHandshake().
+  // TODO(mellem): Move creation of the crypto stream into Initialize() and
+  // remove StartCryptoHandshake() to bring QuartcSession in line with other
+  // implementations of QuicSession, which can be started by calling
+  // Initialize().
+  void Initialize() override;
+
+  // Accessors for the client crypto stream.
+  QuicCryptoStream* GetMutableCryptoStream() override;
+  const QuicCryptoStream* GetCryptoStream() const override;
+
+  // Initializes the session and sends a handshake.
+  void StartCryptoHandshake() override;
+
+  // ProofHandler overrides.
+  void OnProofValid(const QuicCryptoClientConfig::CachedState& cached) override;
+
+  // Called by the client crypto handshake when proof verification details
+  // become available, either because proof verification is complete, or when
+  // cached details are used.
+  void OnProofVerifyDetailsAvailable(
+      const ProofVerifyDetails& verify_details) override;
+
+ private:
+  // Packet writer used by |connection_|.
+  std::unique_ptr<QuartcPacketWriter> packet_writer_;
+
+  // Config for QUIC crypto stream.
+  std::unique_ptr<QuicCryptoClientConfig> client_crypto_config_;
+
+  // Client perspective crypto stream.
+  std::unique_ptr<QuicCryptoClientStream> crypto_stream_;
+
+  const QuicString server_config_;
+};
+
+class QUIC_EXPORT_PRIVATE QuartcServerSession : public QuartcSession {
+ public:
+  QuartcServerSession(std::unique_ptr<QuicConnection> connection,
+                      Visitor* visitor,
+                      const QuicConfig& config,
+                      const ParsedQuicVersionVector& supported_versions,
+                      const QuicClock* clock,
+                      const QuicCryptoServerConfig* server_crypto_config,
+                      QuicCompressedCertsCache* const compressed_certs_cache,
+                      QuicCryptoServerStream::Helper* const stream_helper);
+  QuartcServerSession(const QuartcServerSession&) = delete;
+  QuartcServerSession& operator=(const QuartcServerSession&) = delete;
+
+  // Accessors for the server crypto stream.
+  QuicCryptoStream* GetMutableCryptoStream() override;
+  const QuicCryptoStream* GetCryptoStream() const override;
+
+  // Initializes the session and prepares to receive a handshake.
+  void StartCryptoHandshake() override;
+
+ private:
+  // Config for QUIC crypto stream.
+  const QuicCryptoServerConfig* server_crypto_config_;
+
+  // Used by QUIC crypto server stream to track most recently compressed certs.
+  QuicCompressedCertsCache* const compressed_certs_cache_;
+
+  // This helper is needed to create QuicCryptoServerStream.
+  QuicCryptoServerStream::Helper* const stream_helper_;
+
+  // Server perspective crypto stream.
+  std::unique_ptr<QuicCryptoServerStream> crypto_stream_;
+};
+
 }  // namespace quic
 
 #endif  // QUICHE_QUIC_QUARTC_QUARTC_SESSION_H_
diff --git a/quic/quartc/quartc_session_test.cc b/quic/quartc/quartc_session_test.cc
index 78efa9e..0ca9a58 100644
--- a/quic/quartc/quartc_session_test.cc
+++ b/quic/quartc/quartc_session_test.cc
@@ -16,7 +16,7 @@
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h"
 #include "net/third_party/quiche/src/quic/quartc/counting_packet_filter.h"
-#include "net/third_party/quiche/src/quic/quartc/quartc_factory.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_endpoint.h"
 #include "net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h"
 #include "net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h"
 #include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
@@ -27,8 +27,40 @@
 
 namespace {
 
+constexpr QuicTime::Delta kPropagationDelay =
+    QuicTime::Delta::FromMilliseconds(10);
+// Propagation delay and a bit, but no more than full RTT.
+constexpr QuicTime::Delta kPropagationDelayAndABit =
+    QuicTime::Delta::FromMilliseconds(12);
+
 static QuicByteCount kDefaultMaxPacketSize = 1200;
 
+class FakeQuartcEndpointDelegate : public QuartcEndpoint::Delegate {
+ public:
+  explicit FakeQuartcEndpointDelegate(QuartcSession::Delegate* session_delegate)
+      : session_delegate_(session_delegate) {}
+
+  void OnSessionCreated(QuartcSession* session) override {
+    CHECK_EQ(session_, nullptr);
+    CHECK_NE(session, nullptr);
+    session_ = session;
+    session_->SetDelegate(session_delegate_);
+    session_->StartCryptoHandshake();
+  }
+
+  void OnConnectError(QuicErrorCode error,
+                      const QuicString& error_details) override {
+    LOG(FATAL) << "Unexpected error during QuartcEndpoint::Connect(); error="
+               << error << ", error_details=" << error_details;
+  }
+
+  QuartcSession* session() { return session_; }
+
+ private:
+  QuartcSession::Delegate* session_delegate_;
+  QuartcSession* session_ = nullptr;
+};
+
 class FakeQuartcSessionDelegate : public QuartcSession::Delegate {
  public:
   explicit FakeQuartcSessionDelegate(QuartcStream::Delegate* stream_delegate,
@@ -140,49 +172,61 @@
 
     client_server_link_ = QuicMakeUnique<simulator::SymmetricLink>(
         client_filter_.get(), server_transport_.get(),
-        QuicBandwidth::FromKBitsPerSecond(10 * 1000),
-        QuicTime::Delta::FromMilliseconds(10));
+        QuicBandwidth::FromKBitsPerSecond(10 * 1000), kPropagationDelay);
 
     client_stream_delegate_ = QuicMakeUnique<FakeQuartcStreamDelegate>();
     client_session_delegate_ = QuicMakeUnique<FakeQuartcSessionDelegate>(
         client_stream_delegate_.get(), simulator_.GetClock());
+    client_endpoint_delegate_ = QuicMakeUnique<FakeQuartcEndpointDelegate>(
+        client_session_delegate_.get());
 
     server_stream_delegate_ = QuicMakeUnique<FakeQuartcStreamDelegate>();
     server_session_delegate_ = QuicMakeUnique<FakeQuartcSessionDelegate>(
         server_stream_delegate_.get(), simulator_.GetClock());
+    server_endpoint_delegate_ = QuicMakeUnique<FakeQuartcEndpointDelegate>(
+        server_session_delegate_.get());
 
-    QuartcFactoryConfig factory_config;
-    factory_config.alarm_factory = simulator_.GetAlarmFactory();
-    factory_config.clock = simulator_.GetClock();
-    quartc_factory_ = QuicMakeUnique<QuartcFactory>(factory_config);
+    client_endpoint_ = QuicMakeUnique<QuartcClientEndpoint>(
+        simulator_.GetAlarmFactory(), simulator_.GetClock(),
+        client_endpoint_delegate_.get(), /*serialized_server_config=*/"");
+    server_endpoint_ = QuicMakeUnique<QuartcServerEndpoint>(
+        simulator_.GetAlarmFactory(), simulator_.GetClock(),
+        server_endpoint_delegate_.get());
   }
 
   // Note that input session config will apply to both server and client.
   // Perspective and packet_transport will be overwritten.
-  void CreateClientAndServerSessions(
-      const QuartcSessionConfig& session_config) {
-    Init();
-
-    QuartcSessionConfig client_session_config = session_config;
-    client_session_config.perspective = Perspective::IS_CLIENT;
-    client_session_config.packet_transport = client_transport_.get();
-    client_peer_ = quartc_factory_->CreateQuartcSession(client_session_config);
-    client_peer_->SetDelegate(client_session_delegate_.get());
+  void CreateClientAndServerSessions(const QuartcSessionConfig& session_config,
+                                     bool init = true) {
+    if (init) {
+      Init();
+    }
 
     QuartcSessionConfig server_session_config = session_config;
-    server_session_config.perspective = Perspective::IS_SERVER;
     server_session_config.packet_transport = server_transport_.get();
-    server_peer_ = quartc_factory_->CreateQuartcSession(server_session_config);
-    server_peer_->SetDelegate(server_session_delegate_.get());
+    server_endpoint_->Connect(server_session_config);
+
+    QuartcSessionConfig client_session_config = session_config;
+    client_session_config.packet_transport = client_transport_.get();
+    client_endpoint_->Connect(client_session_config);
+
+    CHECK(simulator_.RunUntil([this] {
+      return client_endpoint_delegate_->session() != nullptr &&
+             server_endpoint_delegate_->session() != nullptr;
+    }));
+
+    client_peer_ = client_endpoint_delegate_->session();
+    server_peer_ = server_endpoint_delegate_->session();
   }
 
   // Runs all tasks scheduled in the next 200 ms.
   void RunTasks() { simulator_.RunFor(QuicTime::Delta::FromMilliseconds(200)); }
 
-  void StartHandshake() {
-    server_peer_->StartCryptoHandshake();
-    client_peer_->StartCryptoHandshake();
-    RunTasks();
+  void AwaitHandshake() {
+    simulator_.RunUntil([this] {
+      return client_peer_->IsCryptoHandshakeConfirmed() &&
+             server_peer_->IsCryptoHandshakeConfirmed();
+    });
   }
 
   // Test handshake establishment and sending/receiving of data for two
@@ -270,7 +314,7 @@
     ASSERT_TRUE(client_peer_->CanSendMessage());
 
     QuartcSession* const peer_sending =
-        direction_from_server ? server_peer_.get() : client_peer_.get();
+        direction_from_server ? server_peer_ : client_peer_;
 
     FakeQuartcSessionDelegate* const delegate_receiving =
         direction_from_server ? client_session_delegate_.get()
@@ -334,51 +378,54 @@
  protected:
   simulator::Simulator simulator_;
 
-  std::unique_ptr<QuartcFactory> quartc_factory_;
-
   std::unique_ptr<simulator::SimulatedQuartcPacketTransport> client_transport_;
   std::unique_ptr<simulator::SimulatedQuartcPacketTransport> server_transport_;
   std::unique_ptr<simulator::CountingPacketFilter> client_filter_;
   std::unique_ptr<simulator::SymmetricLink> client_server_link_;
 
-  std::unique_ptr<QuartcSession> client_peer_;
-  std::unique_ptr<QuartcSession> server_peer_;
-
   std::unique_ptr<FakeQuartcStreamDelegate> client_stream_delegate_;
   std::unique_ptr<FakeQuartcSessionDelegate> client_session_delegate_;
+  std::unique_ptr<FakeQuartcEndpointDelegate> client_endpoint_delegate_;
   std::unique_ptr<FakeQuartcStreamDelegate> server_stream_delegate_;
   std::unique_ptr<FakeQuartcSessionDelegate> server_session_delegate_;
+  std::unique_ptr<FakeQuartcEndpointDelegate> server_endpoint_delegate_;
+
+  std::unique_ptr<QuartcClientEndpoint> client_endpoint_;
+  std::unique_ptr<QuartcServerEndpoint> server_endpoint_;
+
+  QuartcSession* client_peer_ = nullptr;
+  QuartcSession* server_peer_ = nullptr;
 };
 
 TEST_F(QuartcSessionTest, SendReceiveStreams) {
   CreateClientAndServerSessions(QuartcSessionConfig());
-  StartHandshake();
+  AwaitHandshake();
   TestSendReceiveStreams();
 }
 
 TEST_F(QuartcSessionTest, SendReceiveMessages) {
   CreateClientAndServerSessions(QuartcSessionConfig());
-  StartHandshake();
+  AwaitHandshake();
   TestSendReceiveMessage();
 }
 
 TEST_F(QuartcSessionTest, SendReceiveQueuedMessages) {
   CreateClientAndServerSessions(QuartcSessionConfig());
-  StartHandshake();
+  AwaitHandshake();
   TestSendReceiveQueuedMessages(/*direction_from_server=*/true);
   TestSendReceiveQueuedMessages(/*direction_from_server=*/false);
 }
 
 TEST_F(QuartcSessionTest, SendMessageFails) {
   CreateClientAndServerSessions(QuartcSessionConfig());
-  StartHandshake();
+  AwaitHandshake();
   TestSendLongMessage();
 }
 
 TEST_F(QuartcSessionTest, TestCryptoHandshakeCanWriteTriggers) {
   CreateClientAndServerSessions(QuartcSessionConfig());
 
-  StartHandshake();
+  AwaitHandshake();
 
   RunTasks();
 
@@ -399,10 +446,10 @@
 }
 
 TEST_F(QuartcSessionTest, PreSharedKeyHandshake) {
-  CreateClientAndServerSessions(QuartcSessionConfig());
-  client_peer_->SetPreSharedKey("foo");
-  server_peer_->SetPreSharedKey("foo");
-  StartHandshake();
+  QuartcSessionConfig config;
+  config.pre_shared_key = "foo";
+  CreateClientAndServerSessions(config);
+  AwaitHandshake();
   TestSendReceiveStreams();
   TestSendReceiveMessage();
 }
@@ -416,7 +463,7 @@
 
 TEST_F(QuartcSessionTest, CancelQuartcStream) {
   CreateClientAndServerSessions(QuartcSessionConfig());
-  StartHandshake();
+  AwaitHandshake();
   ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
   ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
 
@@ -438,7 +485,7 @@
 // SimulatedQuartcPacketTransport::last_packet_number().
 TEST_F(QuartcSessionTest, WriterGivesPacketNumberToTransport) {
   CreateClientAndServerSessions(QuartcSessionConfig());
-  StartHandshake();
+  AwaitHandshake();
   ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
   ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
 
@@ -459,7 +506,7 @@
 
 TEST_F(QuartcSessionTest, CloseConnection) {
   CreateClientAndServerSessions(QuartcSessionConfig());
-  StartHandshake();
+  AwaitHandshake();
   ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
   ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
 
@@ -471,7 +518,7 @@
 
 TEST_F(QuartcSessionTest, StreamRetransmissionEnabled) {
   CreateClientAndServerSessions(QuartcSessionConfig());
-  StartHandshake();
+  AwaitHandshake();
   ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
   ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
 
@@ -504,10 +551,15 @@
   // message will be retransmitted to to probe for more bandwidth.
   client_peer_->connection()->set_fill_up_link_during_probing(false);
 
-  StartHandshake();
+  AwaitHandshake();
   ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
   ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
 
+  // The client sends an ACK for the crypto handshake next.  This must be
+  // flushed before we set the filter to drop the next packet, in order to
+  // ensure that the filter drops a data-bearing packet instead of just an ack.
+  RunTasks();
+
   QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream();
   QuicStreamId stream_id = stream->id();
   stream->SetDelegate(client_stream_delegate_.get());
@@ -543,6 +595,85 @@
             QUIC_STREAM_CANCELLED);
 }
 
+TEST_F(QuartcSessionTest, ServerRegistersAsWriteBlocked) {
+  // Initialize client and server session, but with the server write-blocked.
+  Init();
+  server_transport_->SetWritable(false);
+  CreateClientAndServerSessions(QuartcSessionConfig(), /*init=*/false);
+
+  // Let the client send a few copies of the CHLO.  The server can't respond, as
+  // it's still write-blocked.
+  RunTasks();
+
+  // Making the server's transport writable should trigger a callback that
+  // reaches the server session, allowing it to write packets.
+  server_transport_->SetWritable(true);
+
+  // Now the server should respond with the SHLO, encryption should be
+  // established, and data should flow normally.
+  // Note that if the server is *not* correctly registered as write-blocked,
+  // it will crash here (see b/124527328 for details).
+  AwaitHandshake();
+  TestSendReceiveStreams();
+}
+
+TEST_F(QuartcSessionTest, PreSharedKeyHandshakeIs0RTT) {
+  QuartcSessionConfig session_config;
+  session_config.pre_shared_key = "foo";
+
+  Init();
+
+  QuartcSessionConfig server_session_config = session_config;
+  server_session_config.packet_transport = server_transport_.get();
+  server_endpoint_->Connect(server_session_config);
+
+  client_endpoint_ = absl::make_unique<QuartcClientEndpoint>(
+      simulator_.GetAlarmFactory(), simulator_.GetClock(),
+      client_endpoint_delegate_.get(),
+      // This is the key line here. It passes through the server config
+      // from the server to the client.
+      server_endpoint_->server_crypto_config());
+
+  QuartcSessionConfig client_session_config = session_config;
+  QuicString();
+  client_session_config.packet_transport = client_transport_.get();
+  client_endpoint_->Connect(client_session_config);
+
+  // Running for 1ms. This is shorter than the RTT, so the
+  // client session should be created, but server won't be created yet.
+  simulator_.RunFor(QuicTime::Delta::FromMilliseconds(1));
+
+  client_peer_ = client_endpoint_delegate_->session();
+  server_peer_ = server_endpoint_delegate_->session();
+
+  ASSERT_NE(client_peer_, nullptr);
+  ASSERT_EQ(server_peer_, nullptr);
+
+  // Write data to the client before running tasks.  This should be sent by the
+  // client and received by the server if the handshake is 0RTT.
+  // If this test fails, add 'RunTasks()' above, and see what error is sent
+  // by the server in the rejection message.
+  QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream();
+  ASSERT_NE(stream, nullptr);
+  QuicStreamId stream_id = stream->id();
+  stream->SetDelegate(client_stream_delegate_.get());
+
+  char message[] = "Hello in 0RTTs!";
+  test::QuicTestMemSliceVector data({std::make_pair(message, strlen(message))});
+  stream->WriteMemSlices(data.span(), /*fin=*/false);
+
+  // This will now run the rest of the connection. But the
+  // Server peer will receive the CHLO and message after 1 delay.
+  simulator_.RunFor(kPropagationDelayAndABit);
+
+  // If we can decrypt the data, it means that 0 rtt was successful.
+  // This is because we waited only a propagation delay. So if the decryption
+  // failed, we would send sREJ instead of SHLO, but it wouldn't be delivered to
+  // the client yet.
+  ASSERT_TRUE(server_stream_delegate_->has_data());
+  EXPECT_EQ(server_stream_delegate_->data()[stream_id], message);
+}
+
 }  // namespace
 
 }  // namespace quic
diff --git a/quic/quartc/quartc_stream.cc b/quic/quartc/quartc_stream.cc
index a0be2c7..49ade23 100644
--- a/quic/quartc/quartc_stream.cc
+++ b/quic/quartc/quartc_stream.cc
@@ -76,14 +76,15 @@
 bool QuartcStream::OnStreamFrameAcked(QuicStreamOffset offset,
                                       QuicByteCount data_length,
                                       bool fin_acked,
-                                      QuicTime::Delta ack_delay_time) {
+                                      QuicTime::Delta ack_delay_time,
+                                      QuicByteCount* newly_acked_length) {
   // Previous losses of acked data are no longer relevant to the retransmission
   // count.  Once data is acked, it will never be retransmitted.
   lost_frame_counter_.RemoveInterval(
       QuicInterval<QuicStreamOffset>(offset, offset + data_length));
 
   return QuicStream::OnStreamFrameAcked(offset, data_length, fin_acked,
-                                        ack_delay_time);
+                                        ack_delay_time, newly_acked_length);
 }
 
 void QuartcStream::OnStreamFrameRetransmitted(QuicStreamOffset offset,
diff --git a/quic/quartc/quartc_stream.h b/quic/quartc/quartc_stream.h
index d34c2c2..e30afbf 100644
--- a/quic/quartc/quartc_stream.h
+++ b/quic/quartc/quartc_stream.h
@@ -47,7 +47,8 @@
   bool OnStreamFrameAcked(QuicStreamOffset offset,
                           QuicByteCount data_length,
                           bool fin_acked,
-                          QuicTime::Delta ack_delay_time) override;
+                          QuicTime::Delta ack_delay_time,
+                          QuicByteCount* newly_acked_length) override;
 
   void OnStreamFrameRetransmitted(QuicStreamOffset offset,
                                   QuicByteCount data_length,
diff --git a/quic/quartc/quartc_stream_test.cc b/quic/quartc/quartc_stream_test.cc
index 075aca1..87e6170 100644
--- a/quic/quartc/quartc_stream_test.cc
+++ b/quic/quartc/quartc_stream_test.cc
@@ -138,9 +138,7 @@
     return WriteResult(WRITE_STATUS_ERROR, 0);
   }
 
-  bool IsWriteBlockedDataBuffered() const override { return false; }
-
-  bool IsWriteBlocked() const override { return false; };
+  bool IsWriteBlocked() const override { return false; }
 
   void SetWritable() override {}
 
@@ -563,8 +561,10 @@
 
   // Ack bytes [0, 7).  These bytes should be pruned from the data tracked by
   // the stream.
-  stream_->OnStreamFrameAcked(0, 7, false,
-                              QuicTime::Delta::FromMilliseconds(1));
+  QuicByteCount newly_acked_length = 0;
+  stream_->OnStreamFrameAcked(0, 7, false, QuicTime::Delta::FromMilliseconds(1),
+                              &newly_acked_length);
+  EXPECT_EQ(7u, newly_acked_length);
   stream_->OnCanWrite();
 
   EXPECT_EQ("Foo barFoo bar", write_buffer_);
diff --git a/quic/quartc/simulated_packet_transport.cc b/quic/quartc/simulated_packet_transport.cc
index 66bfb2b..5c0d374 100644
--- a/quic/quartc/simulated_packet_transport.cc
+++ b/quic/quartc/simulated_packet_transport.cc
@@ -24,6 +24,9 @@
 int SimulatedQuartcPacketTransport::Write(const char* buffer,
                                           size_t buf_len,
                                           const PacketInfo& info) {
+  if (!writable_) {
+    return 0;
+  }
   if (egress_queue_.bytes_queued() + buf_len > egress_queue_.capacity()) {
     return 0;
   }
@@ -69,16 +72,24 @@
 }
 
 void SimulatedQuartcPacketTransport::OnPacketDequeued() {
-  if (delegate_) {
+  if (delegate_ && writable_) {
     delegate_->OnTransportCanWrite();
   }
 }
 
 void SimulatedQuartcPacketTransport::Act() {
-  if (delegate_) {
+  if (delegate_ && writable_) {
     delegate_->OnTransportCanWrite();
   }
 }
 
+void SimulatedQuartcPacketTransport::SetWritable(bool writable) {
+  writable_ = writable;
+  if (writable_) {
+    // May need to call |Delegate::OnTransportCanWrite|.
+    Schedule(clock_->Now());
+  }
+}
+
 }  // namespace simulator
 }  // namespace quic
diff --git a/quic/quartc/simulated_packet_transport.h b/quic/quartc/simulated_packet_transport.h
index 5f1c468..1b190a0 100644
--- a/quic/quartc/simulated_packet_transport.h
+++ b/quic/quartc/simulated_packet_transport.h
@@ -53,6 +53,13 @@
   // callback to unblock it.)
   void Act() override;
 
+  // Changes whether the transport is writable.  If |writable| is false, the
+  // transport will reject calls to |Write| and will not call
+  // |Delegate::OnTransportCanWrite|.  If |writable| is true, the transport will
+  // allow calls to |Write| and will call |Delegate::OnTransportCanWrite|
+  // whenever it is able to write another packet.
+  void SetWritable(bool writable);
+
   // Last packet number sent over this simulated transport.
   // TODO(b/112561077):  Reorganize tests so that this method can be deleted.
   // This exists purely for use by quartc_session_test.cc, to test that the
@@ -64,6 +71,10 @@
   Delegate* delegate_ = nullptr;
   Queue egress_queue_;
   QuicPacketNumber last_packet_number_;
+
+  // Controls whether the transport is considered to be writable.  Used to
+  // simulate behavior that arises when the transport is blocked.
+  bool writable_ = true;
 };
 
 }  // namespace simulator
diff --git a/quic/test_tools/crypto_test_utils.cc b/quic/test_tools/crypto_test_utils.cc
index fd270c3..e36ba52 100644
--- a/quic/test_tools/crypto_test_utils.cc
+++ b/quic/test_tools/crypto_test_utils.cc
@@ -5,6 +5,7 @@
 #include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
 
 #include <memory>
+#include <string>
 
 #include "third_party/boringssl/src/include/openssl/bn.h"
 #include "third_party/boringssl/src/include/openssl/ec.h"
@@ -507,11 +508,19 @@
                                   const CryptoHandshakeMessage& message,
                                   Perspective perspective) {
   const QuicData& data = message.GetSerialized();
-  QuicStreamFrame frame(
-      QuicUtils::GetCryptoStreamId(
-          QuicStreamPeer::session(stream)->connection()->transport_version()),
-      false, stream->stream_bytes_read(), data.AsStringPiece());
-  stream->OnStreamFrame(frame);
+  QuicSession* session = QuicStreamPeer::session(stream);
+  if (session->connection()->transport_version() < QUIC_VERSION_47) {
+    QuicStreamFrame frame(QuicUtils::GetCryptoStreamId(
+                              session->connection()->transport_version()),
+                          false, stream->crypto_bytes_read(),
+                          data.AsStringPiece());
+    stream->OnStreamFrame(frame);
+  } else {
+    EncryptionLevel level = session->connection()->last_decrypted_level();
+    QuicCryptoFrame frame(level, stream->BytesReadOnLevel(level),
+                          data.AsStringPiece());
+    stream->OnCryptoFrame(frame);
+  }
 }
 
 void CommunicateHandshakeMessages(PacketSavingConnection* client_conn,
@@ -589,7 +598,7 @@
 
 uint64_t LeafCertHashForTesting() {
   QuicReferenceCountedPointer<ProofSource::Chain> chain;
-  QuicSocketAddress server_address;
+  QuicSocketAddress server_address(QuicIpAddress::Any4(), 42);
   QuicCryptoProof proof;
   std::unique_ptr<ProofSource> proof_source(ProofSourceForTesting());
 
@@ -724,7 +733,7 @@
   QuicFramer* server_framer = QuicConnectionPeer::GetFramer(
       QuicStreamPeer::session(server)->connection());
   const QuicEncrypter* client_encrypter(
-      QuicFramerPeer::GetEncrypter(client_framer, ENCRYPTION_INITIAL));
+      QuicFramerPeer::GetEncrypter(client_framer, ENCRYPTION_ZERO_RTT));
   const QuicDecrypter* client_decrypter(
       QuicStreamPeer::session(client)->connection()->decrypter());
   const QuicEncrypter* client_forward_secure_encrypter(
@@ -732,7 +741,7 @@
   const QuicDecrypter* client_forward_secure_decrypter(
       QuicStreamPeer::session(client)->connection()->alternative_decrypter());
   const QuicEncrypter* server_encrypter(
-      QuicFramerPeer::GetEncrypter(server_framer, ENCRYPTION_INITIAL));
+      QuicFramerPeer::GetEncrypter(server_framer, ENCRYPTION_ZERO_RTT));
   const QuicDecrypter* server_decrypter(
       QuicStreamPeer::session(server)->connection()->decrypter());
   const QuicEncrypter* server_forward_secure_encrypter(
@@ -936,6 +945,7 @@
       break;
     }
     QuicConnectionPeer::SwapCrypters(dest_conn, framer.framer());
+    dest_conn->OnDecryptedPacket(framer.last_decrypted_level());
 
     if (dest_stream->handshake_protocol() == PROTOCOL_TLS1_3) {
       // Try to process the packet with a framer that only has the NullDecrypter
@@ -961,6 +971,9 @@
     for (const auto& stream_frame : framer.stream_frames()) {
       dest_stream->OnStreamFrame(*stream_frame);
     }
+    for (const auto& crypto_frame : framer.crypto_frames()) {
+      dest_stream->OnCryptoFrame(*crypto_frame);
+    }
   }
   *inout_packet_index = index;
 
diff --git a/quic/test_tools/crypto_test_utils_test.cc b/quic/test_tools/crypto_test_utils_test.cc
index 601103c..39a3b23 100644
--- a/quic/test_tools/crypto_test_utils_test.cc
+++ b/quic/test_tools/crypto_test_utils_test.cc
@@ -113,7 +113,7 @@
       QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance(),
       crypto_test_utils::ProofSourceForTesting(), KeyExchangeSource::Default(),
       TlsServerHandshaker::CreateSslCtx());
-  QuicSocketAddress server_addr;
+  QuicSocketAddress server_addr(QuicIpAddress::Any4(), 5);
   QuicSocketAddress client_addr(QuicIpAddress::Loopback4(), 1);
   QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config(
       new QuicSignedServerConfig);
diff --git a/quic/test_tools/fuzzing/quic_framer_fuzzer.cc b/quic/test_tools/fuzzing/quic_framer_fuzzer.cc
index 1d83802..a14f410 100644
--- a/quic/test_tools/fuzzing/quic_framer_fuzzer.cc
+++ b/quic/test_tools/fuzzing/quic_framer_fuzzer.cc
@@ -1,3 +1,7 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
 #include "base/commandlineflags.h"
 #include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
 #include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
diff --git a/quic/test_tools/mock_quic_dispatcher.cc b/quic/test_tools/mock_quic_dispatcher.cc
index 621a161..d7ccf2c 100644
--- a/quic/test_tools/mock_quic_dispatcher.cc
+++ b/quic/test_tools/mock_quic_dispatcher.cc
@@ -10,7 +10,7 @@
 namespace test {
 
 MockQuicDispatcher::MockQuicDispatcher(
-    const QuicConfig& config,
+    const QuicConfig* config,
     const QuicCryptoServerConfig* crypto_config,
     QuicVersionManager* version_manager,
     std::unique_ptr<QuicConnectionHelperInterface> helper,
diff --git a/quic/test_tools/mock_quic_dispatcher.h b/quic/test_tools/mock_quic_dispatcher.h
index 394a90b..93acda9 100644
--- a/quic/test_tools/mock_quic_dispatcher.h
+++ b/quic/test_tools/mock_quic_dispatcher.h
@@ -19,7 +19,7 @@
 class MockQuicDispatcher : public QuicSimpleDispatcher {
  public:
   MockQuicDispatcher(
-      const QuicConfig& config,
+      const QuicConfig* config,
       const QuicCryptoServerConfig* crypto_config,
       QuicVersionManager* version_manager,
       std::unique_ptr<QuicConnectionHelperInterface> helper,
diff --git a/quic/test_tools/packet_dropping_test_writer.cc b/quic/test_tools/packet_dropping_test_writer.cc
index d296367..87935d5 100644
--- a/quic/test_tools/packet_dropping_test_writer.cc
+++ b/quic/test_tools/packet_dropping_test_writer.cc
@@ -246,7 +246,7 @@
       options(std::move(options)),
       send_time(send_time) {}
 
-PacketDroppingTestWriter::DelayedWrite::~DelayedWrite() {}
+PacketDroppingTestWriter::DelayedWrite::~DelayedWrite() = default;
 
 }  // namespace test
 }  // namespace quic
diff --git a/quic/test_tools/packet_dropping_test_writer.h b/quic/test_tools/packet_dropping_test_writer.h
index 404be81..51b3fc7 100644
--- a/quic/test_tools/packet_dropping_test_writer.h
+++ b/quic/test_tools/packet_dropping_test_writer.h
@@ -12,6 +12,7 @@
 #include "net/third_party/quiche/src/quic/core/quic_alarm.h"
 #include "net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_macros.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_test_client.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
 
@@ -117,9 +118,7 @@
   }
 
   // Useful for reproducing very flaky issues.
-  ABSL_ATTRIBUTE_UNUSED void set_seed(uint64_t seed) {
-    simple_random_.set_seed(seed);
-  }
+  QUIC_UNUSED void set_seed(uint64_t seed) { simple_random_.set_seed(seed); }
 
  private:
   // Writes out the next packet to the contained writer and returns the time
diff --git a/quic/test_tools/quic_framer_peer.cc b/quic/test_tools/quic_framer_peer.cc
index 20cbc49..89b0170 100644
--- a/quic/test_tools/quic_framer_peer.cc
+++ b/quic/test_tools/quic_framer_peer.cc
@@ -213,17 +213,18 @@
 }
 
 // static
-bool QuicFramerPeer::AppendMaxStreamIdFrame(QuicFramer* framer,
-                                            const QuicMaxStreamIdFrame& frame,
-                                            QuicDataWriter* writer) {
-  return framer->AppendMaxStreamIdFrame(frame, writer);
+bool QuicFramerPeer::AppendMaxStreamsFrame(QuicFramer* framer,
+                                           const QuicMaxStreamIdFrame& frame,
+                                           QuicDataWriter* writer) {
+  return framer->AppendMaxStreamsFrame(frame, writer);
 }
 
 // static
-bool QuicFramerPeer::ProcessMaxStreamIdFrame(QuicFramer* framer,
-                                             QuicDataReader* reader,
-                                             QuicMaxStreamIdFrame* frame) {
-  return framer->ProcessMaxStreamIdFrame(reader, frame);
+bool QuicFramerPeer::ProcessMaxStreamsFrame(QuicFramer* framer,
+                                            QuicDataReader* reader,
+                                            QuicMaxStreamIdFrame* frame,
+                                            uint64_t frame_type) {
+  return framer->ProcessMaxStreamsFrame(reader, frame, frame_type);
 }
 
 // static
@@ -255,19 +256,19 @@
 }
 
 // static
-bool QuicFramerPeer::AppendStreamIdBlockedFrame(
+bool QuicFramerPeer::AppendStreamsBlockedFrame(
     QuicFramer* framer,
     const QuicStreamIdBlockedFrame& frame,
     QuicDataWriter* writer) {
-  return framer->AppendStreamIdBlockedFrame(frame, writer);
+  return framer->AppendStreamsBlockedFrame(frame, writer);
 }
 
 // static
-bool QuicFramerPeer::ProcessStreamIdBlockedFrame(
-    QuicFramer* framer,
-    QuicDataReader* reader,
-    QuicStreamIdBlockedFrame* frame) {
-  return framer->ProcessStreamIdBlockedFrame(reader, frame);
+bool QuicFramerPeer::ProcessStreamsBlockedFrame(QuicFramer* framer,
+                                                QuicDataReader* reader,
+                                                QuicStreamIdBlockedFrame* frame,
+                                                uint64_t frame_type) {
+  return framer->ProcessStreamsBlockedFrame(reader, frame, frame_type);
 }
 
 // static
diff --git a/quic/test_tools/quic_framer_peer.h b/quic/test_tools/quic_framer_peer.h
index 1fa7351..cfe259a 100644
--- a/quic/test_tools/quic_framer_peer.h
+++ b/quic/test_tools/quic_framer_peer.h
@@ -116,12 +116,13 @@
   static bool ProcessMaxStreamDataFrame(QuicFramer* framer,
                                         QuicDataReader* reader,
                                         QuicWindowUpdateFrame* frame);
-  static bool AppendMaxStreamIdFrame(QuicFramer* framer,
-                                     const QuicMaxStreamIdFrame& frame,
-                                     QuicDataWriter* writer);
-  static bool ProcessMaxStreamIdFrame(QuicFramer* framer,
-                                      QuicDataReader* reader,
-                                      QuicMaxStreamIdFrame* frame);
+  static bool AppendMaxStreamsFrame(QuicFramer* framer,
+                                    const QuicMaxStreamIdFrame& frame,
+                                    QuicDataWriter* writer);
+  static bool ProcessMaxStreamsFrame(QuicFramer* framer,
+                                     QuicDataReader* reader,
+                                     QuicMaxStreamIdFrame* frame,
+                                     uint64_t frame_type);
   static bool AppendIetfBlockedFrame(QuicFramer* framer,
                                      const QuicBlockedFrame& frame,
                                      QuicDataWriter* writer);
@@ -136,12 +137,13 @@
                                         QuicDataReader* reader,
                                         QuicBlockedFrame* frame);
 
-  static bool AppendStreamIdBlockedFrame(QuicFramer* framer,
-                                         const QuicStreamIdBlockedFrame& frame,
-                                         QuicDataWriter* writer);
-  static bool ProcessStreamIdBlockedFrame(QuicFramer* framer,
-                                          QuicDataReader* reader,
-                                          QuicStreamIdBlockedFrame* frame);
+  static bool AppendStreamsBlockedFrame(QuicFramer* framer,
+                                        const QuicStreamIdBlockedFrame& frame,
+                                        QuicDataWriter* writer);
+  static bool ProcessStreamsBlockedFrame(QuicFramer* framer,
+                                         QuicDataReader* reader,
+                                         QuicStreamIdBlockedFrame* frame,
+                                         uint64_t frame_type);
 
   static bool AppendNewConnectionIdFrame(QuicFramer* framer,
                                          const QuicNewConnectionIdFrame& frame,
diff --git a/quic/test_tools/quic_packet_creator_peer.cc b/quic/test_tools/quic_packet_creator_peer.cc
index efcda92..51d9588 100644
--- a/quic/test_tools/quic_packet_creator_peer.cc
+++ b/quic/test_tools/quic_packet_creator_peer.cc
@@ -43,6 +43,18 @@
   return creator->GetPacketNumberLength();
 }
 
+// static
+QuicVariableLengthIntegerLength
+QuicPacketCreatorPeer::GetRetryTokenLengthLength(QuicPacketCreator* creator) {
+  return creator->GetRetryTokenLengthLength();
+}
+
+// static
+QuicVariableLengthIntegerLength QuicPacketCreatorPeer::GetLengthLength(
+    QuicPacketCreator* creator) {
+  return creator->GetLengthLength();
+}
+
 void QuicPacketCreatorPeer::SetPacketNumber(QuicPacketCreator* creator,
                                             uint64_t s) {
   DCHECK_NE(0u, s);
diff --git a/quic/test_tools/quic_packet_creator_peer.h b/quic/test_tools/quic_packet_creator_peer.h
index 94c461a..c637ac2 100644
--- a/quic/test_tools/quic_packet_creator_peer.h
+++ b/quic/test_tools/quic_packet_creator_peer.h
@@ -27,6 +27,10 @@
       QuicPacketNumberLength packet_number_length);
   static QuicPacketNumberLength GetPacketNumberLength(
       QuicPacketCreator* creator);
+  static QuicVariableLengthIntegerLength GetRetryTokenLengthLength(
+      QuicPacketCreator* creator);
+  static QuicVariableLengthIntegerLength GetLengthLength(
+      QuicPacketCreator* creator);
   static void SetPacketNumber(QuicPacketCreator* creator, uint64_t s);
   static void ClearPacketNumber(QuicPacketCreator* creator);
   static void FillPacketHeader(QuicPacketCreator* creator,
diff --git a/quic/test_tools/quic_sent_packet_manager_peer.cc b/quic/test_tools/quic_sent_packet_manager_peer.cc
index 469fa40..3057e5e 100644
--- a/quic/test_tools/quic_sent_packet_manager_peer.cc
+++ b/quic/test_tools/quic_sent_packet_manager_peer.cc
@@ -8,6 +8,7 @@
 #include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h"
 #include "net/third_party/quiche/src/quic/core/quic_packets.h"
 #include "net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_unacked_packet_map_peer.h"
 
 namespace quic {
 namespace test {
@@ -41,7 +42,8 @@
 void QuicSentPacketManagerPeer::SetPerspective(
     QuicSentPacketManager* sent_packet_manager,
     Perspective perspective) {
-  sent_packet_manager->perspective_ = perspective;
+  QuicUnackedPacketMapPeer::SetPerspective(
+      &sent_packet_manager->unacked_packets_, perspective);
 }
 
 // static
diff --git a/quic/test_tools/quic_spdy_session_peer.cc b/quic/test_tools/quic_spdy_session_peer.cc
index b84a7da..b739fa1 100644
--- a/quic/test_tools/quic_spdy_session_peer.cc
+++ b/quic/test_tools/quic_spdy_session_peer.cc
@@ -50,55 +50,15 @@
 }
 
 // static
-size_t QuicSpdySessionPeer::WriteHeadersImpl(
+size_t QuicSpdySessionPeer::WriteHeadersOnHeadersStream(
     QuicSpdySession* session,
     QuicStreamId id,
     spdy::SpdyHeaderBlock headers,
     bool fin,
-    int weight,
-    QuicStreamId parent_stream_id,
-    bool exclusive,
+    spdy::SpdyPriority priority,
     QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
-  return session->WriteHeadersImpl(id, std::move(headers), fin, weight,
-                                   parent_stream_id, exclusive,
-                                   std::move(ack_listener));
-}
-
-//  static
-QuicStreamId QuicSpdySessionPeer::StreamIdDelta(
-    const QuicSpdySession& session) {
-  return QuicUtils::StreamIdDelta(session.connection()->transport_version());
-}
-
-//  static
-QuicStreamId QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
-    const QuicSpdySession& session,
-    int n) {
-  return QuicUtils::GetFirstBidirectionalStreamId(
-             session.connection()->transport_version(),
-             Perspective::IS_CLIENT) +
-         // + 1 because spdy_session contains headers stream.
-         QuicSpdySessionPeer::StreamIdDelta(session) * (n + 1);
-}
-
-//  static
-QuicStreamId QuicSpdySessionPeer::GetNthServerInitiatedBidirectionalStreamId(
-    const QuicSpdySession& session,
-    int n) {
-  return QuicUtils::GetFirstBidirectionalStreamId(
-             session.connection()->transport_version(),
-             Perspective::IS_SERVER) +
-         QuicSpdySessionPeer::StreamIdDelta(session) * n;
-}
-
-//  static
-QuicStreamId QuicSpdySessionPeer::GetNthServerInitiatedUnidirectionalStreamId(
-    const QuicSpdySession& session,
-    int n) {
-  return QuicUtils::GetFirstUnidirectionalStreamId(
-             session.connection()->transport_version(),
-             Perspective::IS_SERVER) +
-         QuicSpdySessionPeer::StreamIdDelta(session) * n;
+  return session->WriteHeadersOnHeadersStream(
+      id, std::move(headers), fin, priority, std::move(ack_listener));
 }
 
 }  // namespace test
diff --git a/quic/test_tools/quic_spdy_session_peer.h b/quic/test_tools/quic_spdy_session_peer.h
index e0dbddc..1993ec5 100644
--- a/quic/test_tools/quic_spdy_session_peer.h
+++ b/quic/test_tools/quic_spdy_session_peer.h
@@ -35,30 +35,13 @@
   static void SetMaxUncompressedHeaderBytes(
       QuicSpdySession* session,
       size_t set_max_uncompressed_header_bytes);
-  static size_t WriteHeadersImpl(
+  static size_t WriteHeadersOnHeadersStream(
       QuicSpdySession* session,
       QuicStreamId id,
       spdy::SpdyHeaderBlock headers,
       bool fin,
-      int weight,
-      QuicStreamId parent_stream_id,
-      bool exclusive,
+      spdy::SpdyPriority priority,
       QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
-  // Helper functions for stream ids, to allow test logic to abstract
-  // over the HTTP stream numbering scheme (i.e. whether one or
-  // two QUIC streams are used per HTTP transaction).
-  static QuicStreamId StreamIdDelta(const QuicSpdySession& session);
-  // n should start at 0.
-  static QuicStreamId GetNthClientInitiatedBidirectionalStreamId(
-      const QuicSpdySession& session,
-      int n);
-  // n should start at 0.
-  static QuicStreamId GetNthServerInitiatedBidirectionalStreamId(
-      const QuicSpdySession& session,
-      int n);
-  static QuicStreamId GetNthServerInitiatedUnidirectionalStreamId(
-      const QuicSpdySession& session,
-      int n);
 };
 
 }  // namespace test
diff --git a/quic/test_tools/quic_spdy_stream_peer.cc b/quic/test_tools/quic_spdy_stream_peer.cc
new file mode 100644
index 0000000..6632ec1
--- /dev/null
+++ b/quic/test_tools/quic_spdy_stream_peer.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_stream_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h"
+
+namespace quic {
+namespace test {
+
+// static
+void QuicSpdyStreamPeer::set_ack_listener(
+    QuicSpdyStream* stream,
+    QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+  stream->set_ack_listener(std::move(ack_listener));
+}
+
+// static
+const QuicIntervalSet<QuicStreamOffset>&
+QuicSpdyStreamPeer::unacked_frame_headers_offsets(QuicSpdyStream* stream) {
+  return stream->unacked_frame_headers_offsets();
+}
+
+}  // namespace test
+}  // namespace quic
diff --git a/quic/test_tools/quic_spdy_stream_peer.h b/quic/test_tools/quic_spdy_stream_peer.h
new file mode 100644
index 0000000..7b3fe7c
--- /dev/null
+++ b/quic/test_tools/quic_spdy_stream_peer.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_SPDY_STREAM_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_SPDY_STREAM_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_ack_listener_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval_set.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+
+namespace quic {
+
+class QuicSpdyStream;
+
+namespace test {
+
+class QuicSpdyStreamPeer {
+ public:
+  static void set_ack_listener(
+      QuicSpdyStream* stream,
+      QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+  static const QuicIntervalSet<QuicStreamOffset>& unacked_frame_headers_offsets(
+      QuicSpdyStream* stream);
+};
+
+}  // namespace test
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_TEST_TOOLS_QUIC_SPDY_STREAM_PEER_H_
diff --git a/quic/test_tools/quic_stream_peer.cc b/quic/test_tools/quic_stream_peer.cc
index e512ba4..7607523 100644
--- a/quic/test_tools/quic_stream_peer.cc
+++ b/quic/test_tools/quic_stream_peer.cc
@@ -87,12 +87,5 @@
   return stream->send_buffer_;
 }
 
-// static
-void QuicStreamPeer::set_ack_listener(
-    QuicStream* stream,
-    QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
-  stream->set_ack_listener(std::move(ack_listener));
-}
-
 }  // namespace test
 }  // namespace quic
diff --git a/quic/test_tools/quic_stream_peer.h b/quic/test_tools/quic_stream_peer.h
index e1ba33a..e0058ba 100644
--- a/quic/test_tools/quic_stream_peer.h
+++ b/quic/test_tools/quic_stream_peer.h
@@ -48,10 +48,6 @@
   static QuicSession* session(QuicStream* stream);
 
   static QuicStreamSendBuffer& SendBuffer(QuicStream* stream);
-
-  static void set_ack_listener(
-      QuicStream* stream,
-      QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
 };
 
 }  // namespace test
diff --git a/quic/test_tools/quic_test_client.cc b/quic/test_tools/quic_test_client.cc
index a6298a6..213a7fd 100644
--- a/quic/test_tools/quic_test_client.cc
+++ b/quic/test_tools/quic_test_client.cc
@@ -25,12 +25,10 @@
 #include "net/third_party/quiche/src/quic/test_tools/quic_client_peer.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
-#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_stream_peer.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
 #include "net/third_party/quiche/src/quic/tools/quic_url.h"
 
-using spdy::SpdyHeaderBlock;
-
 namespace quic {
 namespace test {
 namespace {
@@ -341,9 +339,8 @@
       session->connection(), QuicConnection::SEND_ACK_IF_PENDING);
   ssize_t ret = SendMessage(headers, "", /*fin=*/true, /*flush=*/false);
 
-  QuicStreamId stream_id =
-      QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(*session,
-                                                                      0);
+  QuicStreamId stream_id = GetNthClientInitiatedBidirectionalStreamId(
+      session->connection()->transport_version(), 0);
   session->SendRstStream(stream_id, QUIC_STREAM_CANCELLED, 0);
   return ret;
 }
@@ -385,18 +382,18 @@
   if (stream == nullptr) {
     return 0;
   }
-  QuicStreamPeer::set_ack_listener(stream, ack_listener);
+  QuicSpdyStreamPeer::set_ack_listener(stream, ack_listener);
 
   ssize_t ret = 0;
   if (headers != nullptr) {
-    SpdyHeaderBlock spdy_headers(headers->Clone());
+    spdy::SpdyHeaderBlock spdy_headers(headers->Clone());
     if (spdy_headers[":authority"].as_string().empty()) {
       spdy_headers[":authority"] = client_->server_id().host();
     }
     ret = stream->SendRequest(std::move(spdy_headers), body, fin);
     ++num_requests_;
   } else {
-    stream->WriteOrBufferBody(QuicString(body), fin, ack_listener);
+    stream->WriteOrBufferBody(QuicString(body), fin);
     ret = body.length();
   }
   if (GetQuicReloadableFlag(enable_quic_stateless_reject_support)) {
@@ -741,9 +738,9 @@
   open_streams_.erase(id);
 }
 
-bool QuicTestClient::CheckVary(const SpdyHeaderBlock& client_request,
-                               const SpdyHeaderBlock& promise_request,
-                               const SpdyHeaderBlock& promise_response) {
+bool QuicTestClient::CheckVary(const spdy::SpdyHeaderBlock& client_request,
+                               const spdy::SpdyHeaderBlock& promise_request,
+                               const spdy::SpdyHeaderBlock& promise_response) {
   return true;
 }
 
diff --git a/quic/test_tools/quic_test_server.cc b/quic/test_tools/quic_test_server.cc
index d2d33d6..220969b 100644
--- a/quic/test_tools/quic_test_server.cc
+++ b/quic/test_tools/quic_test_server.cc
@@ -70,7 +70,7 @@
 class QuicTestDispatcher : public QuicSimpleDispatcher {
  public:
   QuicTestDispatcher(
-      const QuicConfig& config,
+      const QuicConfig* config,
       const QuicCryptoServerConfig* crypto_config,
       QuicVersionManager* version_manager,
       std::unique_ptr<QuicConnectionHelperInterface> helper,
@@ -165,7 +165,7 @@
 
 QuicDispatcher* QuicTestServer::CreateQuicDispatcher() {
   return new QuicTestDispatcher(
-      config(), &crypto_config(), version_manager(),
+      &config(), &crypto_config(), version_manager(),
       QuicMakeUnique<QuicEpollConnectionHelper>(epoll_server(),
                                                 QuicAllocator::BUFFER_POOL),
       std::unique_ptr<QuicCryptoServerStream::Helper>(
diff --git a/quic/test_tools/quic_test_utils.cc b/quic/test_tools/quic_test_utils.cc
index da76b33..b10b509 100644
--- a/quic/test_tools/quic_test_utils.cc
+++ b/quic/test_tools/quic_test_utils.cc
@@ -41,9 +41,6 @@
 }
 
 QuicConnectionId TestConnectionId(uint64_t connection_number) {
-  if (!QuicConnectionIdUseNetworkByteOrder()) {
-    return QuicConnectionIdFromUInt64(connection_number);
-  }
   const uint64_t connection_id64_net =
       QuicEndian::HostToNet64(connection_number);
   return QuicConnectionId(reinterpret_cast<const char*>(&connection_id64_net),
@@ -51,9 +48,6 @@
 }
 
 uint64_t TestConnectionIdToUInt64(QuicConnectionId connection_id) {
-  if (!QuicConnectionIdUseNetworkByteOrder()) {
-    return QuicConnectionIdToUInt64(connection_id);
-  }
   DCHECK_EQ(connection_id.length(), kQuicDefaultConnectionIdLength);
   uint64_t connection_id64_net = 0;
   memcpy(&connection_id64_net, connection_id.data(),
@@ -124,14 +118,17 @@
     const QuicFrames& frames,
     size_t packet_size) {
   char* buffer = new char[packet_size];
-  size_t length = framer->BuildDataPacket(header, frames, buffer, packet_size);
+  size_t length = framer->BuildDataPacket(header, frames, buffer, packet_size,
+                                          ENCRYPTION_NONE);
   DCHECK_NE(0u, length);
   // Re-construct the data packet with data ownership.
   return QuicMakeUnique<QuicPacket>(
       buffer, length, /* owns_buffer */ true,
       header.destination_connection_id_length,
       header.source_connection_id_length, header.version_flag,
-      header.nonce != nullptr, header.packet_number_length);
+      header.nonce != nullptr, header.packet_number_length,
+      header.retry_token_length_length, header.retry_token.length(),
+      header.length_length);
 }
 
 QuicString Sha1Hash(QuicStringPiece data) {
@@ -221,6 +218,8 @@
   return true;
 }
 
+void NoOpFramerVisitor::OnCoalescedPacket(const QuicEncryptedPacket& packet) {}
+
 bool NoOpFramerVisitor::OnStreamFrame(const QuicStreamFrame& frame) {
   return true;
 }
@@ -433,6 +432,11 @@
   ON_CALL(*this, OnError(_))
       .WillByDefault(
           Invoke(this, &PacketSavingConnection::QuicConnection_OnError));
+  ON_CALL(*this, SendCryptoData(_, _, _))
+      .WillByDefault(
+          Invoke(this, &MockQuicConnection::QuicConnection_SendCryptoData));
+
+  SetSelfAddress(QuicSocketAddress(QuicIpAddress::Any4(), 5));
 }
 
 MockQuicConnection::~MockQuicConnection() {}
@@ -466,10 +470,11 @@
 void PacketSavingConnection::SendOrQueuePacket(SerializedPacket* packet) {
   encrypted_packets_.push_back(QuicMakeUnique<QuicEncryptedPacket>(
       CopyBuffer(*packet), packet->encrypted_length, true));
+  clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
   // Transfer ownership of the packet to the SentPacketManager and the
   // ack notifier to the AckNotifierManager.
   QuicConnectionPeer::GetSentPacketManager(this)->OnPacketSent(
-      packet, QuicPacketNumber(), QuicTime::Zero(), NOT_RETRANSMISSION,
+      packet, QuicPacketNumber(), clock_.ApproximateNow(), NOT_RETRANSMISSION,
       HAS_RETRANSMITTABLE_DATA);
 }
 
@@ -581,16 +586,6 @@
   crypto_stream_.reset(crypto_stream);
 }
 
-size_t MockQuicSpdySession::WriteHeaders(
-    QuicStreamId id,
-    spdy::SpdyHeaderBlock headers,
-    bool fin,
-    spdy::SpdyPriority priority,
-    QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
-  write_headers_ = std::move(headers);
-  return WriteHeadersMock(id, write_headers_, fin, priority, ack_listener);
-}
-
 TestQuicSpdyServerSession::TestQuicSpdyServerSession(
     QuicConnection* connection,
     const QuicConfig& config,
@@ -853,17 +848,27 @@
   header.reset_flag = reset_flag;
   header.packet_number_length = packet_number_length;
   header.packet_number = QuicPacketNumber(packet_number);
-  QuicFrame frame(QuicStreamFrame(
-      QuicUtils::GetCryptoStreamId(
-          versions != nullptr
-              ? (*versions)[0].transport_version
-              : CurrentSupportedVersions()[0].transport_version),
-      false, 0, QuicStringPiece(data)));
+  ParsedQuicVersionVector supported_versions = CurrentSupportedVersions();
+  if (!versions) {
+    versions = &supported_versions;
+  }
+  if (QuicVersionHasLongHeaderLengths((*versions)[0].transport_version) &&
+      version_flag) {
+    header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1;
+    header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+  }
+
   QuicFrames frames;
-  frames.push_back(frame);
-  QuicFramer framer(
-      versions != nullptr ? *versions : CurrentSupportedVersions(),
-      QuicTime::Zero(), perspective);
+  QuicFramer framer(*versions, QuicTime::Zero(), perspective);
+  if ((*versions)[0].transport_version < QUIC_VERSION_47) {
+    QuicFrame frame(QuicStreamFrame(
+        QuicUtils::GetCryptoStreamId((*versions)[0].transport_version), false,
+        0, QuicStringPiece(data)));
+    frames.push_back(frame);
+  } else {
+    QuicFrame frame(new QuicCryptoFrame(ENCRYPTION_NONE, 0, data));
+    frames.push_back(frame);
+  }
 
   std::unique_ptr<QuicPacket> packet(
       BuildUnsizedDataPacket(&framer, header, frames));
@@ -873,6 +878,7 @@
       framer.EncryptPayload(ENCRYPTION_NONE, QuicPacketNumber(packet_number),
                             *packet, buffer, kMaxPacketSize);
   EXPECT_NE(0u, encrypted_length);
+  DeleteFrames(&frames);
   return new QuicEncryptedPacket(buffer, encrypted_length, true);
 }
 
@@ -921,7 +927,9 @@
       packet->mutable_data())[GetStartOfEncryptedData(
       framer.transport_version(), destination_connection_id_length,
       source_connection_id_length, version_flag,
-      false /* no diversification nonce */, packet_number_length)] = 0x1F;
+      false /* no diversification nonce */, packet_number_length,
+      VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0)] =
+      0x1F;
 
   char* buffer = new char[kMaxPacketSize];
   size_t encrypted_length =
@@ -969,6 +977,8 @@
     QuicConnectionIdLength destination_connection_id_length,
     QuicConnectionIdLength source_connection_id_length,
     QuicPacketNumberLength packet_number_length,
+    QuicVariableLengthIntegerLength retry_token_length_length,
+    QuicVariableLengthIntegerLength length_length,
     size_t* payload_length) {
   *payload_length = 1;
   const size_t stream_length =
@@ -976,14 +986,16 @@
       QuicPacketCreator::StreamFramePacketOverhead(
           version, destination_connection_id_length,
           source_connection_id_length, include_version,
-          include_diversification_nonce, packet_number_length, 0u);
+          include_diversification_nonce, packet_number_length,
+          retry_token_length_length, length_length, 0u);
   const size_t ack_length =
       NullEncrypter(Perspective::IS_CLIENT)
           .GetCiphertextSize(QuicFramer::GetMinAckFrameSize(
               version, PACKET_1BYTE_PACKET_NUMBER)) +
       GetPacketHeaderSize(version, destination_connection_id_length,
                           source_connection_id_length, include_version,
-                          include_diversification_nonce, packet_number_length);
+                          include_diversification_nonce, packet_number_length,
+                          retry_token_length_length, 0, length_length);
   if (stream_length < ack_length) {
     *payload_length = 1 + ack_length - stream_length;
   }
@@ -993,7 +1005,8 @@
          QuicPacketCreator::StreamFramePacketOverhead(
              version, destination_connection_id_length,
              source_connection_id_length, include_version,
-             include_diversification_nonce, packet_number_length, 0u);
+             include_diversification_nonce, packet_number_length,
+             retry_token_length_length, length_length, 0u);
 }
 
 QuicConfig DefaultQuicConfig() {
@@ -1104,12 +1117,52 @@
   (*server_connection)->AdvanceTime(connection_start_time);
 }
 
+QuicStreamId GetNthClientInitiatedBidirectionalStreamId(
+    QuicTransportVersion version,
+    int n) {
+  return QuicUtils::GetFirstBidirectionalStreamId(version,
+                                                  Perspective::IS_CLIENT) +
+         // + 1 because spdy_session contains headers stream.
+         QuicUtils::StreamIdDelta(version) * (n + 1);
+}
+
+QuicStreamId GetNthServerInitiatedBidirectionalStreamId(
+    QuicTransportVersion version,
+    int n) {
+  return QuicUtils::GetFirstBidirectionalStreamId(version,
+                                                  Perspective::IS_SERVER) +
+         QuicUtils::StreamIdDelta(version) * n;
+}
+
+QuicStreamId GetNthServerInitiatedUnidirectionalStreamId(
+    QuicTransportVersion version,
+    int n) {
+  return QuicUtils::GetFirstUnidirectionalStreamId(version,
+                                                   Perspective::IS_SERVER) +
+         QuicUtils::StreamIdDelta(version) * n;
+}
+
 StreamType DetermineStreamType(QuicStreamId id,
                                QuicTransportVersion version,
+                               Perspective perspective,
                                bool is_incoming,
                                StreamType default_type) {
-  return version == QUIC_VERSION_99 ? QuicUtils::GetStreamType(id, is_incoming)
-                                    : default_type;
+  return version == QUIC_VERSION_99
+             ? QuicUtils::GetStreamType(id, perspective, is_incoming)
+             : default_type;
+}
+
+QuicMemSliceSpan MakeSpan(QuicBufferAllocator* allocator,
+                          QuicStringPiece message_data,
+                          QuicMemSliceStorage* storage) {
+  if (message_data.length() == 0) {
+    *storage = QuicMemSliceStorage(nullptr, 0, allocator, kMaxPacketSize);
+    return storage->ToSpan();
+  }
+  struct iovec iov = {const_cast<char*>(message_data.data()),
+                      message_data.length()};
+  *storage = QuicMemSliceStorage(&iov, 1, allocator, kMaxPacketSize);
+  return storage->ToSpan();
 }
 
 }  // namespace test
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
index 8b52d4f..cf4c167 100644
--- a/quic/test_tools/quic_test_utils.h
+++ b/quic/test_tools/quic_test_utils.h
@@ -25,6 +25,7 @@
 #include "net/third_party/quiche/src/quic/core/quic_packet_writer.h"
 #include "net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h"
 #include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
 #include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
@@ -164,6 +165,8 @@
     QuicConnectionIdLength destination_connection_id_length,
     QuicConnectionIdLength source_connection_id_length,
     QuicPacketNumberLength packet_number_length,
+    QuicVariableLengthIntegerLength retry_token_length_length,
+    QuicVariableLengthIntegerLength length_length,
     size_t* payload_length);
 
 // Returns QuicConfig set to default values.
@@ -264,6 +267,7 @@
                bool(const QuicPacketHeader& header));
   MOCK_METHOD1(OnDecryptedPacket, void(EncryptionLevel level));
   MOCK_METHOD1(OnPacketHeader, bool(const QuicPacketHeader& header));
+  MOCK_METHOD1(OnCoalescedPacket, void(const QuicEncryptedPacket& packet));
   MOCK_METHOD1(OnStreamFrame, bool(const QuicStreamFrame& frame));
   MOCK_METHOD1(OnCryptoFrame, bool(const QuicCryptoFrame& frame));
   MOCK_METHOD2(OnAckFrameStart, bool(QuicPacketNumber, QuicTime::Delta));
@@ -316,6 +320,7 @@
   bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override;
   void OnDecryptedPacket(EncryptionLevel level) override {}
   bool OnPacketHeader(const QuicPacketHeader& header) override;
+  void OnCoalescedPacket(const QuicEncryptedPacket& packet) override;
   bool OnStreamFrame(const QuicStreamFrame& frame) override;
   bool OnCryptoFrame(const QuicCryptoFrame& frame) override;
   bool OnAckFrameStart(QuicPacketNumber largest_acked,
@@ -358,6 +363,7 @@
   ~MockQuicConnectionVisitor() override;
 
   MOCK_METHOD1(OnStreamFrame, void(const QuicStreamFrame& frame));
+  MOCK_METHOD1(OnCryptoFrame, void(const QuicCryptoFrame& frame));
   MOCK_METHOD1(OnWindowUpdateFrame, void(const QuicWindowUpdateFrame& frame));
   MOCK_METHOD1(OnBlockedFrame, void(const QuicBlockedFrame& frame));
   MOCK_METHOD1(OnRstStream, void(const QuicRstStreamFrame& frame));
@@ -511,7 +517,7 @@
 
   MOCK_METHOD2(OnStreamReset, void(QuicStreamId, QuicRstStreamErrorCode));
   MOCK_METHOD1(SendControlFrame, bool(const QuicFrame& frame));
-  MOCK_METHOD2(SendMessage, MessageStatus(QuicMessageId, QuicStringPiece));
+  MOCK_METHOD2(SendMessage, MessageStatus(QuicMessageId, QuicMemSliceSpan));
   MOCK_METHOD3(OnConnectionClosed,
                void(QuicErrorCode error,
                     const QuicString& error_details,
@@ -555,6 +561,13 @@
   }
   MOCK_METHOD1(OnPathResponseFrame, bool(const QuicPathResponseFrame&));
   MOCK_METHOD1(OnStopSendingFrame, bool(const QuicStopSendingFrame& frame));
+  MOCK_METHOD3(SendCryptoData,
+               size_t(EncryptionLevel, size_t, QuicStreamOffset));
+  size_t QuicConnection_SendCryptoData(EncryptionLevel level,
+                                       size_t write_length,
+                                       QuicStreamOffset offset) {
+    return QuicConnection::SendCryptoData(level, write_length, offset);
+  }
 };
 
 class PacketSavingConnection : public MockQuicConnection {
@@ -575,6 +588,7 @@
   void SendOrQueuePacket(SerializedPacket* packet) override;
 
   std::vector<std::unique_ptr<QuicEncryptedPacket>> encrypted_packets_;
+  MockClock clock_;
 };
 
 class MockQuicSession : public QuicSession {
@@ -668,7 +682,6 @@
   QuicCryptoStream* GetMutableCryptoStream() override;
   const QuicCryptoStream* GetCryptoStream() const override;
   void SetCryptoStream(QuicCryptoStream* crypto_stream);
-  const spdy::SpdyHeaderBlock& GetWriteHeaders() { return write_headers_; }
 
   // From QuicSession.
   MOCK_METHOD3(OnConnectionClosed,
@@ -720,22 +733,6 @@
   MOCK_METHOD2(OnPriorityFrame,
                void(QuicStreamId id, spdy::SpdyPriority priority));
 
-  // Methods taking non-copyable types like SpdyHeaderBlock by value cannot be
-  // mocked directly.
-  size_t WriteHeaders(QuicStreamId id,
-                      spdy::SpdyHeaderBlock headers,
-                      bool fin,
-                      spdy::SpdyPriority priority,
-                      QuicReferenceCountedPointer<QuicAckListenerInterface>
-                          ack_listener) override;
-  MOCK_METHOD5(
-      WriteHeadersMock,
-      size_t(QuicStreamId id,
-             const spdy::SpdyHeaderBlock& headers,
-             bool fin,
-             spdy::SpdyPriority priority,
-             const QuicReferenceCountedPointer<QuicAckListenerInterface>&
-                 ack_listener));
   MOCK_METHOD1(OnHeadersHeadOfLineBlocking, void(QuicTime::Delta delta));
   MOCK_METHOD4(
       OnStreamFrameData,
@@ -745,7 +742,6 @@
 
  private:
   std::unique_ptr<QuicCryptoStream> crypto_stream_;
-  spdy::SpdyHeaderBlock write_headers_;
 };
 
 class TestQuicSpdyServerSession : public QuicServerSessionBase {
@@ -869,7 +865,6 @@
                            const QuicIpAddress& self_address,
                            const QuicSocketAddress& peer_address,
                            PerPacketOptions* options));
-  MOCK_CONST_METHOD0(IsWriteBlockedDataBuffered, bool());
   MOCK_CONST_METHOD0(IsWriteBlocked, bool());
   MOCK_METHOD0(SetWritable, void());
   MOCK_CONST_METHOD1(GetMaxPacketSize,
@@ -1008,8 +1003,6 @@
 
   MOCK_METHOD1(OnStreamFrame, void(const QuicStreamFrame&));
 
-  MOCK_METHOD1(OnAckFrame, void(const QuicAckFrame& frame));
-
   MOCK_METHOD1(OnStopWaitingFrame, void(const QuicStopWaitingFrame&));
 
   MOCK_METHOD1(OnRstStreamFrame, void(const QuicRstStreamFrame&));
@@ -1177,11 +1170,31 @@
   iov->iov_len = static_cast<size_t>(str.size());
 }
 
+// Helper functions for stream ids, to allow test logic to abstract over the
+// HTTP stream numbering scheme (i.e. whether one or two QUIC streams are used
+// per HTTP transaction).
+QuicStreamId GetNthClientInitiatedBidirectionalStreamId(
+    QuicTransportVersion version,
+    int n);
+QuicStreamId GetNthServerInitiatedBidirectionalStreamId(
+    QuicTransportVersion version,
+    int n);
+QuicStreamId GetNthServerInitiatedUnidirectionalStreamId(
+    QuicTransportVersion version,
+    int n);
+
 StreamType DetermineStreamType(QuicStreamId id,
                                QuicTransportVersion version,
+                               Perspective perspective,
                                bool is_incoming,
                                StreamType default_type);
 
+// Utility function that stores message_data in |storage| and returns a
+// QuicMemSliceSpan.
+QuicMemSliceSpan MakeSpan(QuicBufferAllocator* allocator,
+                          QuicStringPiece message_data,
+                          QuicMemSliceStorage* storage);
+
 }  // namespace test
 }  // namespace quic
 
diff --git a/quic/test_tools/quic_unacked_packet_map_peer.cc b/quic/test_tools/quic_unacked_packet_map_peer.cc
new file mode 100644
index 0000000..1ecc65f
--- /dev/null
+++ b/quic/test_tools/quic_unacked_packet_map_peer.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_unacked_packet_map_peer.h"
+
+namespace quic {
+namespace test {
+
+// static
+const QuicStreamFrame& QuicUnackedPacketMapPeer::GetAggregatedStreamFrame(
+    const QuicUnackedPacketMap& unacked_packets) {
+  return unacked_packets.aggregated_stream_frame_;
+}
+
+// static
+void QuicUnackedPacketMapPeer::SetPerspective(
+    QuicUnackedPacketMap* unacked_packets,
+    Perspective perspective) {
+  *const_cast<Perspective*>(&unacked_packets->perspective_) = perspective;
+}
+
+}  // namespace test
+}  // namespace quic
diff --git a/quic/test_tools/quic_unacked_packet_map_peer.h b/quic/test_tools/quic_unacked_packet_map_peer.h
new file mode 100644
index 0000000..3bba0b6
--- /dev/null
+++ b/quic/test_tools/quic_unacked_packet_map_peer.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_UNACKED_PACKET_MAP_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_UNACKED_PACKET_MAP_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h"
+
+namespace quic {
+namespace test {
+
+class QuicUnackedPacketMapPeer {
+ public:
+  static const QuicStreamFrame& GetAggregatedStreamFrame(
+      const QuicUnackedPacketMap& unacked_packets);
+
+  static void SetPerspective(QuicUnackedPacketMap* unacked_packets,
+                             Perspective perspective);
+};
+
+}  // namespace test
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_TEST_TOOLS_QUIC_UNACKED_PACKET_MAP_PEER_H_
diff --git a/quic/test_tools/simple_quic_framer.cc b/quic/test_tools/simple_quic_framer.cc
index 6b0fb66..b5847ac 100644
--- a/quic/test_tools/simple_quic_framer.cc
+++ b/quic/test_tools/simple_quic_framer.cc
@@ -55,6 +55,8 @@
     return true;
   }
 
+  void OnCoalescedPacket(const QuicEncryptedPacket& packet) override {}
+
   bool OnStreamFrame(const QuicStreamFrame& frame) override {
     // Save a copy of the data so it is valid after the packet is processed.
     QuicString* string_data =
@@ -68,8 +70,13 @@
   }
 
   bool OnCryptoFrame(const QuicCryptoFrame& frame) override {
-    // TODO(nharper): Implement this.
-    return false;
+    // Save a copy of the data so it is valid after the packet is processed.
+    QuicString* string_data =
+        new QuicString(frame.data_buffer, frame.data_length);
+    crypto_data_.push_back(QuicWrapUnique(string_data));
+    crypto_frames_.push_back(QuicMakeUnique<QuicCryptoFrame>(
+        frame.level, frame.offset, QuicStringPiece(*string_data)));
+    return true;
   }
 
   bool OnAckFrameStart(QuicPacketNumber largest_acked,
@@ -181,7 +188,7 @@
   }
 
   bool OnMessageFrame(const QuicMessageFrame& frame) override {
-    message_frames_.push_back(frame);
+    message_frames_.emplace_back(frame.data, frame.message_length);
     return true;
   }
 
@@ -222,6 +229,9 @@
   const std::vector<std::unique_ptr<QuicStreamFrame>>& stream_frames() const {
     return stream_frames_;
   }
+  const std::vector<std::unique_ptr<QuicCryptoFrame>>& crypto_frames() const {
+    return crypto_frames_;
+  }
   const std::vector<QuicStopWaitingFrame>& stop_waiting_frames() const {
     return stop_waiting_frames_;
   }
@@ -258,6 +268,7 @@
   std::vector<QuicPaddingFrame> padding_frames_;
   std::vector<QuicPingFrame> ping_frames_;
   std::vector<std::unique_ptr<QuicStreamFrame>> stream_frames_;
+  std::vector<std::unique_ptr<QuicCryptoFrame>> crypto_frames_;
   std::vector<QuicRstStreamFrame> rst_stream_frames_;
   std::vector<QuicGoAwayFrame> goaway_frames_;
   std::vector<QuicStreamIdBlockedFrame> stream_id_blocked_frames_;
@@ -274,6 +285,7 @@
   std::vector<QuicNewTokenFrame> new_token_frames_;
   std::vector<QuicMessageFrame> message_frames_;
   std::vector<std::unique_ptr<QuicString>> stream_data_;
+  std::vector<std::unique_ptr<QuicString>> crypto_data_;
   EncryptionLevel last_decrypted_level_;
 };
 
@@ -325,7 +337,8 @@
          rst_stream_frames().size() + stop_waiting_frames().size() +
          path_challenge_frames().size() + path_response_frames().size() +
          stream_frames().size() + ping_frames().size() +
-         connection_close_frames().size() + padding_frames().size();
+         connection_close_frames().size() + padding_frames().size() +
+         crypto_frames().size();
 }
 
 const std::vector<QuicAckFrame>& SimpleQuicFramer::ack_frames() const {
@@ -364,6 +377,11 @@
   return visitor_->stream_frames();
 }
 
+const std::vector<std::unique_ptr<QuicCryptoFrame>>&
+SimpleQuicFramer::crypto_frames() const {
+  return visitor_->crypto_frames();
+}
+
 const std::vector<QuicRstStreamFrame>& SimpleQuicFramer::rst_stream_frames()
     const {
   return visitor_->rst_stream_frames();
diff --git a/quic/test_tools/simple_quic_framer.h b/quic/test_tools/simple_quic_framer.h
index da2f4e5..2edf9d3 100644
--- a/quic/test_tools/simple_quic_framer.h
+++ b/quic/test_tools/simple_quic_framer.h
@@ -47,6 +47,7 @@
   const std::vector<QuicGoAwayFrame>& goaway_frames() const;
   const std::vector<QuicRstStreamFrame>& rst_stream_frames() const;
   const std::vector<std::unique_ptr<QuicStreamFrame>>& stream_frames() const;
+  const std::vector<std::unique_ptr<QuicCryptoFrame>>& crypto_frames() const;
   const std::vector<QuicPaddingFrame>& padding_frames() const;
   const QuicVersionNegotiationPacket* version_negotiation_packet() const;
   EncryptionLevel last_decrypted_level() const;
diff --git a/quic/test_tools/simple_session_notifier.cc b/quic/test_tools/simple_session_notifier.cc
index 75c55f4..c56831e 100644
--- a/quic/test_tools/simple_session_notifier.cc
+++ b/quic/test_tools/simple_session_notifier.cc
@@ -81,6 +81,17 @@
   state.fin_outstanding = fin;
 }
 
+size_t SimpleSessionNotifier::WriteCryptoData(EncryptionLevel level,
+                                              QuicByteCount data_length,
+                                              QuicStreamOffset offset) {
+  crypto_state_[level].bytes_total += data_length;
+  size_t bytes_written =
+      connection_->SendCryptoData(level, data_length, offset);
+  crypto_state_[level].bytes_sent += bytes_written;
+  crypto_bytes_transferred_[level].Add(offset, offset + bytes_written);
+  return bytes_written;
+}
+
 void SimpleSessionNotifier::WriteOrBufferRstStream(
     QuicStreamId id,
     QuicRstStreamErrorCode error,
@@ -103,6 +114,7 @@
 
 void SimpleSessionNotifier::NeuterUnencryptedData() {
   for (const auto& interval : crypto_bytes_transferred_[ENCRYPTION_NONE]) {
+    // TODO(nharper): Handle CRYPTO frame case.
     QuicStreamFrame stream_frame(
         QuicUtils::GetCryptoStreamId(connection_->transport_version()), false,
         interval.min(), interval.max() - interval.min());
@@ -120,6 +132,7 @@
     return;
   }
   // Write new data.
+  // TODO(nharper): Write CRYPTO frames.
   for (const auto& pair : stream_map_) {
     const auto& state = pair.second;
     if (!StreamHasBufferedData(pair.first)) {
@@ -175,6 +188,19 @@
 bool SimpleSessionNotifier::OnFrameAcked(const QuicFrame& frame,
                                          QuicTime::Delta /*ack_delay_time*/) {
   QUIC_DVLOG(1) << "Acking " << frame;
+  if (frame.type == CRYPTO_FRAME) {
+    StreamState* state = &crypto_state_[frame.crypto_frame->level];
+    QuicStreamOffset offset = frame.crypto_frame->offset;
+    QuicByteCount data_length = frame.crypto_frame->data_length;
+    QuicIntervalSet<QuicStreamOffset> newly_acked(offset, offset + data_length);
+    newly_acked.Difference(state->bytes_acked);
+    if (newly_acked.Empty()) {
+      return false;
+    }
+    state->bytes_acked.Add(offset, offset + data_length);
+    state->pending_retransmissions.Difference(offset, offset + data_length);
+    return true;
+  }
   if (frame.type != STREAM_FRAME) {
     return OnControlFrameAcked(frame);
   }
@@ -201,6 +227,20 @@
 
 void SimpleSessionNotifier::OnFrameLost(const QuicFrame& frame) {
   QUIC_DVLOG(1) << "Losting " << frame;
+  if (frame.type == CRYPTO_FRAME) {
+    StreamState* state = &crypto_state_[frame.crypto_frame->level];
+    QuicStreamOffset offset = frame.crypto_frame->offset;
+    QuicByteCount data_length = frame.crypto_frame->data_length;
+    QuicIntervalSet<QuicStreamOffset> bytes_lost(offset, offset + data_length);
+    bytes_lost.Difference(state->bytes_acked);
+    if (bytes_lost.Empty()) {
+      return;
+    }
+    for (const auto& lost : bytes_lost) {
+      state->pending_retransmissions.Add(lost.min(), lost.max());
+    }
+    return;
+  }
   if (frame.type != STREAM_FRAME) {
     OnControlFrameLost(frame);
     return;
@@ -229,6 +269,21 @@
       connection_, QuicConnection::SEND_ACK_IF_QUEUED);
   connection_->SetTransmissionType(type);
   for (const QuicFrame& frame : frames) {
+    if (frame.type == CRYPTO_FRAME) {
+      const StreamState& state = crypto_state_[frame.crypto_frame->level];
+      QuicIntervalSet<QuicStreamOffset> retransmission(
+          frame.crypto_frame->offset,
+          frame.crypto_frame->offset + frame.crypto_frame->data_length);
+      retransmission.Difference(state.bytes_acked);
+      for (const auto& interval : retransmission) {
+        QuicStreamOffset offset = interval.min();
+        QuicByteCount length = interval.max() - interval.min();
+        size_t consumed = connection_->SendCryptoData(frame.crypto_frame->level,
+                                                      length, offset);
+        // CRYPTO frames should never be write blocked.
+        DCHECK_EQ(consumed, length);
+      }
+    }
     if (frame.type != STREAM_FRAME) {
       if (GetControlFrameId(frame) == kInvalidControlFrameId) {
         continue;
@@ -308,6 +363,14 @@
 }
 
 bool SimpleSessionNotifier::IsFrameOutstanding(const QuicFrame& frame) const {
+  if (frame.type == CRYPTO_FRAME) {
+    QuicStreamOffset offset = frame.crypto_frame->offset;
+    QuicByteCount data_length = frame.crypto_frame->data_length;
+    bool ret = data_length > 0 &&
+               !crypto_state_[frame.crypto_frame->level].bytes_acked.Contains(
+                   offset, offset + data_length);
+    return ret;
+  }
   if (frame.type != STREAM_FRAME) {
     return IsControlFrameOutstanding(frame);
   }
@@ -323,6 +386,20 @@
 }
 
 bool SimpleSessionNotifier::HasUnackedCryptoData() const {
+  if (connection_->transport_version() >= QUIC_VERSION_47) {
+    for (size_t i = 0; i < NUM_ENCRYPTION_LEVELS; ++i) {
+      const StreamState& state = crypto_state_[i];
+      if (state.bytes_total > state.bytes_sent) {
+        return true;
+      }
+      QuicIntervalSet<QuicStreamOffset> bytes_to_ack(0, state.bytes_total);
+      bytes_to_ack.Difference(state.bytes_acked);
+      if (!bytes_to_ack.Empty()) {
+        return true;
+      }
+    }
+    return false;
+  }
   if (!QuicContainsKey(stream_map_, QuicUtils::GetCryptoStreamId(
                                         connection_->transport_version()))) {
     return false;
@@ -406,6 +483,7 @@
 }
 
 bool SimpleSessionNotifier::RetransmitLostCryptoData() {
+  // TODO(nharper): Handle CRYPTO frame case.
   if (!QuicContainsKey(stream_map_, QuicUtils::GetCryptoStreamId(
                                         connection_->transport_version()))) {
     return true;
diff --git a/quic/test_tools/simple_session_notifier.h b/quic/test_tools/simple_session_notifier.h
index 293937f..f7982d2 100644
--- a/quic/test_tools/simple_session_notifier.h
+++ b/quic/test_tools/simple_session_notifier.h
@@ -6,6 +6,7 @@
 #define QUICHE_QUIC_TEST_TOOLS_SIMPLE_SESSION_NOTIFIER_H_
 
 #include "testing/gmock/include/gmock/gmock.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval_set.h"
 #include "net/third_party/quiche/src/quic/core/session_notifier_interface.h"
 
 namespace quic {
@@ -31,6 +32,11 @@
                               QuicRstStreamErrorCode error,
                               QuicStreamOffset bytes_written);
 
+  // Tries to write CRYPTO data and returns the number of bytes written.
+  size_t WriteCryptoData(EncryptionLevel level,
+                         QuicByteCount data_length,
+                         QuicStreamOffset offset);
+
   // Neuters unencrypted data of crypto stream.
   void NeuterUnencryptedData();
 
@@ -133,6 +139,8 @@
   QuicIntervalSet<QuicStreamOffset>
       crypto_bytes_transferred_[NUM_ENCRYPTION_LEVELS];
 
+  StreamState crypto_state_[NUM_ENCRYPTION_LEVELS];
+
   QuicConnection* connection_;
 };
 
diff --git a/quic/test_tools/simple_session_notifier_test.cc b/quic/test_tools/simple_session_notifier_test.cc
index 61bc95c..b20fee6 100644
--- a/quic/test_tools/simple_session_notifier_test.cc
+++ b/quic/test_tools/simple_session_notifier_test.cc
@@ -111,8 +111,8 @@
   notifier_.WriteOrBufferData(
       QuicUtils::GetCryptoStreamId(connection_.transport_version()), 1024,
       NO_FIN);
-  // Send crypto data [1024, 2048) in ENCRYPTION_INITIAL.
-  connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+  // Send crypto data [1024, 2048) in ENCRYPTION_ZERO_RTT.
+  connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
   EXPECT_CALL(connection_, SendStreamData(QuicUtils::GetCryptoStreamId(
                                               connection_.transport_version()),
                                           1024, 1024, NO_FIN))
@@ -144,8 +144,8 @@
   notifier_.WriteOrBufferData(
       QuicUtils::GetCryptoStreamId(connection_.transport_version()), 1024,
       NO_FIN);
-  // Send crypto data [1024, 2048) in ENCRYPTION_INITIAL.
-  connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+  // Send crypto data [1024, 2048) in ENCRYPTION_ZERO_RTT.
+  connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
   EXPECT_CALL(connection_, SendStreamData(QuicUtils::GetCryptoStreamId(
                                               connection_.transport_version()),
                                           1024, 1024, NO_FIN))
diff --git a/quic/test_tools/simulator/quic_endpoint.cc b/quic/test_tools/simulator/quic_endpoint.cc
index 2867d82..5cd4de1 100644
--- a/quic/test_tools/simulator/quic_endpoint.cc
+++ b/quic/test_tools/simulator/quic_endpoint.cc
@@ -223,6 +223,8 @@
   DCHECK_LE(offsets_received_.Size(), 1000u);
 }
 
+void QuicEndpoint::OnCryptoFrame(const QuicCryptoFrame& frame) {}
+
 void QuicEndpoint::OnCanWrite() {
   if (notifier_ != nullptr) {
     notifier_->OnCanWrite();
@@ -311,10 +313,6 @@
   return WriteResult(WRITE_STATUS_OK, buf_len);
 }
 
-bool QuicEndpoint::Writer::IsWriteBlockedDataBuffered() const {
-  return false;
-}
-
 bool QuicEndpoint::Writer::IsWriteBlocked() const {
   return is_blocked_;
 }
diff --git a/quic/test_tools/simulator/quic_endpoint.h b/quic/test_tools/simulator/quic_endpoint.h
index 600a1d1..e36f384 100644
--- a/quic/test_tools/simulator/quic_endpoint.h
+++ b/quic/test_tools/simulator/quic_endpoint.h
@@ -80,6 +80,7 @@
 
   // Begin QuicConnectionVisitorInterface implementation.
   void OnStreamFrame(const QuicStreamFrame& frame) override;
+  void OnCryptoFrame(const QuicCryptoFrame& frame) override;
   void OnCanWrite() override;
   bool WillingAndAbleToWrite() const override;
   bool HasPendingHandshake() const override;
@@ -108,13 +109,13 @@
   void OnForwardProgressConfirmed() override {}
   bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) override {
     return true;
-  };
+  }
   bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame) override {
     return true;
-  };
+  }
   bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override {
     return true;
-  };
+  }
 
   // End QuicConnectionVisitorInterface implementation.
 
@@ -141,7 +142,6 @@
                             const QuicIpAddress& self_address,
                             const QuicSocketAddress& peer_address,
                             PerPacketOptions* options) override;
-    bool IsWriteBlockedDataBuffered() const override;
     bool IsWriteBlocked() const override;
     void SetWritable() override;
     QuicByteCount GetMaxPacketSize(
diff --git a/quic/test_tools/simulator/switch.cc b/quic/test_tools/simulator/switch.cc
index d00daf7..638fa20 100644
--- a/quic/test_tools/simulator/switch.cc
+++ b/quic/test_tools/simulator/switch.cc
@@ -4,7 +4,6 @@
 
 #include <cinttypes>
 
-#include "base/port.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
 #include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h"
@@ -17,10 +16,9 @@
                SwitchPortNumber port_count,
                QuicByteCount queue_capacity) {
   for (size_t port_number = 1; port_number <= port_count; port_number++) {
-    ports_.emplace_back(
-        simulator,
-        QuicStringPrintf("%s (port %" PRIuS ")", name.c_str(), port_number),
-        this, port_number, queue_capacity);
+    ports_.emplace_back(simulator,
+                        QuicStrCat(name, " (port ", port_number, ")"), this,
+                        port_number, queue_capacity);
   }
 }
 
diff --git a/quic/tools/quic_client_base.cc b/quic/tools/quic_client_base.cc
index f824811..2d8c307 100644
--- a/quic/tools/quic_client_base.cc
+++ b/quic/tools/quic_client_base.cc
@@ -226,6 +226,11 @@
   return true;
 }
 
+bool QuicClientBase::ChangeEphemeralPort() {
+  auto current_host = network_helper_->GetLatestClientAddress().host();
+  return MigrateSocketWithSpecifiedPort(current_host, 0 /*any ephemeral port*/);
+}
+
 QuicSession* QuicClientBase::session() {
   return session_.get();
 }
@@ -325,7 +330,7 @@
 }
 
 QuicConnectionId QuicClientBase::GenerateNewConnectionId() {
-  return QuicUtils::CreateRandomConnectionId(Perspective::IS_CLIENT);
+  return QuicUtils::CreateRandomConnectionId();
 }
 
 bool QuicClientBase::CanReconnectWithDifferentVersion(
diff --git a/quic/tools/quic_client_base.h b/quic/tools/quic_client_base.h
index c751bfd..379f097 100644
--- a/quic/tools/quic_client_base.h
+++ b/quic/tools/quic_client_base.h
@@ -10,12 +10,12 @@
 
 #include <string>
 
-#include "base/macros.h"
 #include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
 #include "net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h"
 #include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h"
 #include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
 #include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_macros.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
 
@@ -102,7 +102,7 @@
 
   // Wait for events until the handshake is confirmed.
   // Returns true if the crypto handshake succeeds, false otherwise.
-  bool WaitForCryptoHandshakeConfirmed() ABSL_MUST_USE_RESULT;
+  bool WaitForCryptoHandshakeConfirmed() QUIC_MUST_USE_RESULT;
 
   // Wait up to 50ms, and handle any events which occur.
   // Returns true if there are any outstanding requests.
@@ -114,6 +114,9 @@
   // Migrate to a new socket (new_host, port) during an active connection.
   bool MigrateSocketWithSpecifiedPort(const QuicIpAddress& new_host, int port);
 
+  // Open a new socket to change to a new ephemeral port.
+  bool ChangeEphemeralPort();
+
   QuicSession* session();
 
   bool connected() const;
diff --git a/quic/tools/quic_client_bin.cc b/quic/tools/quic_client_bin.cc
index 629b980..9504c74 100644
--- a/quic/tools/quic_client_bin.cc
+++ b/quic/tools/quic_client_bin.cc
@@ -29,6 +29,9 @@
 //   IP=`dig www.google.com +short | head -1`
 //   quic_client www.google.com --host=${IP}
 //
+// Send repeated requests and change ephemeral port between requests
+//   quic_client www.google.com --num_requests=10
+//
 // Try to connect to a host which does not speak QUIC:
 //   quic_client www.example.com
 //
@@ -40,19 +43,21 @@
 //     --config=quic quic_client
 
 #include <iostream>
+#include <vector>
 
 #include "base/commandlineflags.h"
 #include "base/init_google.h"
 #include "net/base/ipaddress.h"
 #include "net/dns/hostlookup.h"
 #include "third_party/absl/flags/flag.h"
-#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier_google3.h"
 #include "net/third_party/quiche/src/quic/core/quic_packets.h"
 #include "net/third_party/quiche/src/quic/core/quic_server_id.h"
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_default_proof_providers.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
 #include "net/third_party/quiche/src/quic/tools/quic_client.h"
@@ -90,49 +95,84 @@
   }
 };
 
-DEFINE_string(host,
-              "",
-              "The IP or hostname to connect to. If not provided, the host "
-              "will be derived from the provided URL.");
-DEFINE_int32(port, 0, "The port to connect to.");
-DEFINE_string(body, "", "If set, send a POST with this body.");
-DEFINE_string(body_hex,
-              "",
-              "If set, contents are converted from hex to ascii, before "
-              "sending as body of a POST. e.g. --body_hex=\"68656c6c6f\"");
-DEFINE_string(headers,
-              "",
-              "A semicolon separated list of key:value pairs to "
-              "add to request headers.");
-DEFINE_bool(quiet, false, "Set to true for a quieter output experience.");
-DEFINE_int32(quic_version,
-             -1,
-             "QUIC version to speak, e.g. 21. If not set, then all available "
-             "versions are offered in the handshake.");
-DEFINE_bool(version_mismatch_ok,
-            false,
-            "If true, a version mismatch in the handshake is not considered a "
-            "failure. Useful for probing a server to determine if it speaks "
-            "any version of QUIC.");
-DEFINE_bool(redirect_is_success,
-            true,
-            "If true, an HTTP response code of 3xx is considered to be a "
-            "successful response, otherwise a failure.");
-DEFINE_int32(initial_mtu, 0, "Initial MTU of the connection.");
-DEFINE_string(root_certificate_file,
-              "/google/src/head/depot/google3/security/cacerts/"
-              "for_connecting_to_google/roots.pem",
-              "Path to the root certificate which the server's certificate is "
-              "required to chain to.");
-ABSL_FLAG(bool,
-          disable_certificate_verification,
-          false,
-          "If true, don't verify the server certificate.");
-ABSL_FLAG(bool,
-          drop_response_body,
-          false,
-          "If true, drop response body immediately after it is received.");
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+    string,
+    host,
+    "",
+    "The IP or hostname to connect to. If not provided, the host "
+    "will be derived from the provided URL.");
 
+DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t, port, 0, "The port to connect to.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(string,
+                              body,
+                              "",
+                              "If set, send a POST with this body.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+    string,
+    body_hex,
+    "",
+    "If set, contents are converted from hex to ascii, before "
+    "sending as body of a POST. e.g. --body_hex=\"68656c6c6f\"");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+    string,
+    headers,
+    "",
+    "A semicolon separated list of key:value pairs to "
+    "add to request headers.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(bool,
+                              quiet,
+                              false,
+                              "Set to true for a quieter output experience.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+    int32_t,
+    quic_version,
+    -1,
+    "QUIC version to speak, e.g. 21. If not set, then all available "
+    "versions are offered in the handshake.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+    bool,
+    version_mismatch_ok,
+    false,
+    "If true, a version mismatch in the handshake is not considered a "
+    "failure. Useful for probing a server to determine if it speaks "
+    "any version of QUIC.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+    bool,
+    redirect_is_success,
+    true,
+    "If true, an HTTP response code of 3xx is considered to be a "
+    "successful response, otherwise a failure.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t,
+                              initial_mtu,
+                              0,
+                              "Initial MTU of the connection.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+    int32_t,
+    num_requests,
+    1,
+    "How many sequential requests to make on a single connection.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(bool,
+                              disable_certificate_verification,
+                              false,
+                              "If true, don't verify the server certificate.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+    bool,
+    drop_response_body,
+    false,
+    "If true, drop response body immediately after it is received.");
+
+using quic::QuicString;
 using quic::QuicStringPiece;
 using quic::QuicTextUtils;
 using quic::QuicUrl;
@@ -141,20 +181,22 @@
 using std::endl;
 
 int main(int argc, char* argv[]) {
-  InitGoogle(argv[0], &argc, &argv, true);
+  const char* usage = "Usage: quic_client [options] <url>";
 
   // All non-flag arguments should be interpreted as URLs to fetch.
-  if (argc != 2) {
-    cerr << "Usage: " << argv[0] << " [optional flags] url" << endl;
-    return 1;
+  std::vector<QuicString> urls =
+      quic::QuicParseCommandLineFlags(usage, argc, argv);
+  if (urls.size() != 1) {
+    quic::QuicPrintCommandLineFlagHelp(usage);
+    exit(0);
   }
 
-  QuicUrl url(argv[1], "https");
-  string host = FLAGS_host;
+  QuicUrl url(urls[0], "https");
+  string host = GetQuicFlag(FLAGS_host);
   if (host.empty()) {
     host = url.host();
   }
-  int port = FLAGS_port;
+  int port = GetQuicFlag(FLAGS_port);
   if (port == 0) {
     port = url.port();
   }
@@ -176,23 +218,24 @@
   quic::QuicEpollServer epoll_server;
   quic::QuicServerId server_id(url.host(), port, false);
   quic::ParsedQuicVersionVector versions = quic::CurrentSupportedVersions();
-  if (FLAGS_quic_version != -1) {
+  if (GetQuicFlag(FLAGS_quic_version) != -1) {
     versions.clear();
     versions.push_back(quic::ParsedQuicVersion(
-        quic::PROTOCOL_QUIC_CRYPTO,
-        static_cast<quic::QuicTransportVersion>(FLAGS_quic_version)));
+        quic::PROTOCOL_QUIC_CRYPTO, static_cast<quic::QuicTransportVersion>(
+                                        GetQuicFlag(FLAGS_quic_version))));
   }
+  const int32_t num_requests(GetQuicFlag(FLAGS_num_requests));
   std::unique_ptr<quic::ProofVerifier> proof_verifier;
   if (GetQuicFlag(FLAGS_disable_certificate_verification)) {
     proof_verifier = quic::QuicMakeUnique<FakeProofVerifier>();
   } else {
-    proof_verifier = quic::QuicMakeUnique<quic::ProofVerifierGoogle3>(
-        FLAGS_root_certificate_file);
+    proof_verifier = quic::CreateDefaultProofVerifier();
   }
   quic::QuicClient client(quic::QuicSocketAddress(ip_addr, port), server_id,
                           versions, &epoll_server, std::move(proof_verifier));
+  int32_t initial_mtu = GetQuicFlag(FLAGS_initial_mtu);
   client.set_initial_max_packet_length(
-      FLAGS_initial_mtu != 0 ? FLAGS_initial_mtu : quic::kDefaultMaxPacketSize);
+      initial_mtu != 0 ? initial_mtu : quic::kDefaultMaxPacketSize);
   client.set_drop_response_body(GetQuicFlag(FLAGS_drop_response_body));
   if (!client.Initialize()) {
     cerr << "Failed to initialize client." << endl;
@@ -206,7 +249,7 @@
            << endl;
       // 0: No error.
       // 20: Failed to connect due to QUIC_INVALID_VERSION.
-      return FLAGS_version_mismatch_ok ? 0 : 20;
+      return GetQuicFlag(FLAGS_version_mismatch_ok) ? 0 : 20;
     }
     cerr << "Failed to connect to " << host_port
          << ". Error: " << quic::QuicErrorCodeToString(error) << endl;
@@ -215,10 +258,11 @@
   cout << "Connected to " << host_port << endl;
 
   // Construct the string body from flags, if provided.
-  string body = FLAGS_body;
-  if (!FLAGS_body_hex.empty()) {
-    DCHECK(FLAGS_body.empty()) << "Only set one of --body and --body_hex.";
-    body = QuicTextUtils::HexDecode(FLAGS_body_hex);
+  string body = GetQuicFlag(FLAGS_body);
+  if (!GetQuicFlag(FLAGS_body_hex).empty()) {
+    DCHECK(GetQuicFlag(FLAGS_body).empty())
+        << "Only set one of --body and --body_hex.";
+    body = QuicTextUtils::HexDecode(GetQuicFlag(FLAGS_body_hex));
   }
 
   // Construct a GET or POST request for supplied URL.
@@ -229,7 +273,8 @@
   header_block[":path"] = url.PathParamsQuery();
 
   // Append any additional headers supplied on the command line.
-  for (QuicStringPiece sp : QuicTextUtils::Split(FLAGS_headers, ';')) {
+  for (QuicStringPiece sp :
+       QuicTextUtils::Split(GetQuicFlag(FLAGS_headers), ';')) {
     QuicTextUtils::RemoveLeadingAndTrailingWhitespace(&sp);
     if (sp.empty()) {
       continue;
@@ -243,55 +288,72 @@
   // Make sure to store the response, for later output.
   client.set_store_response(true);
 
-  // Send the request.
-  client.SendRequestAndWaitForResponse(header_block, body, /*fin=*/true);
+  for (int i = 0; i < num_requests; ++i) {
+    // Send the request.
+    client.SendRequestAndWaitForResponse(header_block, body, /*fin=*/true);
 
-  // Print request and response details.
-  if (!FLAGS_quiet) {
-    cout << "Request:" << endl;
-    cout << "headers:" << header_block.DebugString();
-    if (!FLAGS_body_hex.empty()) {
-      // Print the user provided hex, rather than binary body.
-      cout << "body:\n"
-           << QuicTextUtils::HexDump(QuicTextUtils::HexDecode(FLAGS_body_hex))
-           << endl;
-    } else {
-      cout << "body: " << body << endl;
-    }
-    cout << endl;
-
-    if (!client.preliminary_response_headers().empty()) {
-      cout << "Preliminary response headers: "
-           << client.preliminary_response_headers() << endl;
+    // Print request and response details.
+    if (!GetQuicFlag(FLAGS_quiet)) {
+      cout << "Request:" << endl;
+      cout << "headers:" << header_block.DebugString();
+      if (!GetQuicFlag(FLAGS_body_hex).empty()) {
+        // Print the user provided hex, rather than binary body.
+        cout << "body:\n"
+             << QuicTextUtils::HexDump(
+                    QuicTextUtils::HexDecode(GetQuicFlag(FLAGS_body_hex)))
+             << endl;
+      } else {
+        cout << "body: " << body << endl;
+      }
       cout << endl;
+
+      if (!client.preliminary_response_headers().empty()) {
+        cout << "Preliminary response headers: "
+             << client.preliminary_response_headers() << endl;
+        cout << endl;
+      }
+
+      cout << "Response:" << endl;
+      cout << "headers: " << client.latest_response_headers() << endl;
+      string response_body = client.latest_response_body();
+      if (!GetQuicFlag(FLAGS_body_hex).empty()) {
+        // Assume response is binary data.
+        cout << "body:\n" << QuicTextUtils::HexDump(response_body) << endl;
+      } else {
+        cout << "body: " << response_body << endl;
+      }
+      cout << "trailers: " << client.latest_response_trailers() << endl;
     }
 
-    cout << "Response:" << endl;
-    cout << "headers: " << client.latest_response_headers() << endl;
-    string response_body = client.latest_response_body();
-    if (!FLAGS_body_hex.empty()) {
-      // Assume response is binary data.
-      cout << "body:\n" << QuicTextUtils::HexDump(response_body) << endl;
-    } else {
-      cout << "body: " << response_body << endl;
-    }
-    cout << "trailers: " << client.latest_response_trailers() << endl;
-  }
-
-  size_t response_code = client.latest_response_code();
-  if (response_code >= 200 && response_code < 300) {
-    cout << "Request succeeded (" << response_code << ")." << endl;
-    return 0;
-  } else if (response_code >= 300 && response_code < 400) {
-    if (FLAGS_redirect_is_success) {
-      cout << "Request succeeded (redirect " << response_code << ")." << endl;
-      return 0;
-    } else {
-      cout << "Request failed (redirect " << response_code << ")." << endl;
+    if (!client.connected()) {
+      cerr << "Request caused connection failure. Error: "
+           << quic::QuicErrorCodeToString(client.session()->error()) << endl;
       return 1;
     }
-  } else {
-    cerr << "Request failed (" << response_code << ")." << endl;
-    return 1;
+
+    size_t response_code = client.latest_response_code();
+    if (response_code >= 200 && response_code < 300) {
+      cout << "Request succeeded (" << response_code << ")." << endl;
+    } else if (response_code >= 300 && response_code < 400) {
+      if (GetQuicFlag(FLAGS_redirect_is_success)) {
+        cout << "Request succeeded (redirect " << response_code << ")." << endl;
+      } else {
+        cout << "Request failed (redirect " << response_code << ")." << endl;
+        return 1;
+      }
+    } else {
+      cerr << "Request failed (" << response_code << ")." << endl;
+      return 1;
+    }
+
+    // Change the ephemeral port if there are more requests to do.
+    if (i + 1 < num_requests) {
+      if (!client.ChangeEphemeralPort()) {
+        cout << "Failed to change ephemeral port." << endl;
+        return 1;
+      }
+    }
   }
+
+  return 0;
 }
diff --git a/quic/tools/quic_client_epoll_network_helper.cc b/quic/tools/quic_client_epoll_network_helper.cc
index 6a48ad3..f2d22a1 100644
--- a/quic/tools/quic_client_epoll_network_helper.cc
+++ b/quic/tools/quic_client_epoll_network_helper.cc
@@ -78,6 +78,7 @@
   } else {
     client_address = QuicSocketAddress(QuicIpAddress::Any6(), bind_to_port);
   }
+
   sockaddr_storage addr = client_address.generic_address();
   int rc = bind(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
   if (rc < 0) {
diff --git a/quic/tools/quic_client_epoll_network_helper.h b/quic/tools/quic_client_epoll_network_helper.h
index 5871cd0..e0f8cf3 100644
--- a/quic/tools/quic_client_epoll_network_helper.h
+++ b/quic/tools/quic_client_epoll_network_helper.h
@@ -13,16 +13,13 @@
 #include <string>
 
 #include "base/macros.h"
-#include "gfe/gfe2/base/epoll_server.h"
 #include "net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h"
-#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h"
-#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
 #include "net/third_party/quiche/src/quic/core/quic_config.h"
 #include "net/third_party/quiche/src/quic/core/quic_packet_reader.h"
 #include "net/third_party/quiche/src/quic/core/quic_process_packet_interface.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h"
-#include "net/third_party/quiche/src/quic/tools/quic_spdy_client_base.h"
+#include "net/third_party/quiche/src/quic/tools/quic_client_base.h"
 
 namespace quic {
 
diff --git a/quic/tools/quic_client_test.cc b/quic/tools/quic_client_test.cc
index 4f139b3..7977f05 100644
--- a/quic/tools/quic_client_test.cc
+++ b/quic/tools/quic_client_test.cc
@@ -4,13 +4,14 @@
 
 #include "net/third_party/quiche/src/quic/tools/quic_client.h"
 
+#include <dirent.h>
+#include <sys/types.h>
+
 #include <memory>
 
-#include "file/base/path.h"
-#include "file/util/linux_fileops.h"
-#include "gfe/gfe2/base/epoll_server.h"
-#include "net/util/netutil.h"
-#include "testing/base/public/test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_port_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
@@ -23,39 +24,44 @@
 
 const char* kPathToFds = "/proc/self/fd";
 
+QuicString ReadLink(const QuicString& path) {
+  QuicString result(PATH_MAX, '\0');
+  ssize_t result_size = readlink(path.c_str(), &result[0], result.size());
+  CHECK(result_size > 0 && static_cast<size_t>(result_size) < result.size());
+  result.resize(result_size);
+  return result;
+}
+
 // Counts the number of open sockets for the current process.
 size_t NumOpenSocketFDs() {
-  std::vector<QuicString> fd_entries;
-  QuicString error_message;
-
-  CHECK(file_util::LinuxFileOps::ListDirectoryEntries(kPathToFds, &fd_entries,
-                                                      &error_message));
-
   size_t socket_count = 0;
-  for (const QuicString& entry : fd_entries) {
-    if (entry == "." || entry == "..") {
+  dirent* file;
+  std::unique_ptr<DIR, int (*)(DIR*)> fd_directory(opendir(kPathToFds),
+                                                   closedir);
+  while ((file = readdir(fd_directory.get())) != nullptr) {
+    QuicStringPiece name(file->d_name);
+    if (name == "." || name == "..") {
       continue;
     }
 
-    QuicString fd_path =
-        file_util::LinuxFileOps::ReadLink(file::JoinPath(kPathToFds, entry));
+    QuicString fd_path = ReadLink(QuicStrCat(kPathToFds, "/", name));
     if (QuicTextUtils::StartsWith(fd_path, "socket:")) {
       socket_count++;
     }
   }
-
   return socket_count;
 }
 
 // Creates a new QuicClient and Initializes it. Caller is responsible for
 // deletion.
-QuicClient* CreateAndInitializeQuicClient(QuicEpollServer* eps, uint16_t port) {
+std::unique_ptr<QuicClient> CreateAndInitializeQuicClient(QuicEpollServer* eps,
+                                                          uint16_t port) {
   QuicSocketAddress server_address(QuicSocketAddress(TestLoopback(), port));
   QuicServerId server_id("hostname", server_address.port(), false);
   ParsedQuicVersionVector versions = AllSupportedVersions();
-  QuicClient* client =
-      new QuicClient(server_address, server_id, versions, eps,
-                     crypto_test_utils::ProofVerifierForTesting());
+  auto client =
+      QuicMakeUnique<QuicClient>(server_address, server_id, versions, eps,
+                                 crypto_test_utils::ProofVerifierForTesting());
   EXPECT_TRUE(client->Initialize());
   return client;
 }
@@ -66,8 +72,12 @@
   // Make sure that the QuicClient doesn't leak socket FDs. Doing so could cause
   // port exhaustion in long running processes which repeatedly create clients.
 
-  // Record initial number of FDs, after creation of EpollServer.
+  // Record initial number of FDs, after creating EpollServer and creating and
+  // destroying a single client (the latter is needed since initializing
+  // platform dependencies like certificate verifier may open a persistent
+  // socket).
   QuicEpollServer eps;
+  CreateAndInitializeQuicClient(&eps, QuicPickUnusedPortOrDie());
   size_t number_of_open_fds = NumOpenSocketFDs();
 
   // Create a number of clients, initialize them, and verify this has resulted
@@ -75,7 +85,7 @@
   const int kNumClients = 50;
   for (int i = 0; i < kNumClients; ++i) {
     std::unique_ptr<QuicClient> client(
-        CreateAndInitializeQuicClient(&eps, net_util::PickUnusedPortOrDie()));
+        CreateAndInitializeQuicClient(&eps, QuicPickUnusedPortOrDie()));
 
     // Initializing the client will create a new FD.
     EXPECT_LT(number_of_open_fds, NumOpenSocketFDs());
@@ -90,7 +100,7 @@
   size_t number_of_open_fds = NumOpenSocketFDs();
 
   std::unique_ptr<QuicClient> client(
-      CreateAndInitializeQuicClient(&eps, net_util::PickUnusedPortOrDie()));
+      CreateAndInitializeQuicClient(&eps, QuicPickUnusedPortOrDie()));
   EXPECT_EQ(number_of_open_fds + 1, NumOpenSocketFDs());
   // Create more UDP sockets.
   EXPECT_TRUE(QuicClientPeer::CreateUDPSocketAndBind(client.get()));
diff --git a/quic/tools/quic_memory_cache_backend.h b/quic/tools/quic_memory_cache_backend.h
index 1bf9b7c..1417349 100644
--- a/quic/tools/quic_memory_cache_backend.h
+++ b/quic/tools/quic_memory_cache_backend.h
@@ -68,7 +68,6 @@
    private:
     QuicStringPiece host_;
     QuicStringPiece path_;
-    QuicMemoryCacheBackend* cache_;
   };
 
   QuicMemoryCacheBackend();
diff --git a/quic/tools/quic_memory_cache_backend_test.cc b/quic/tools/quic_memory_cache_backend_test.cc
index 8c621be..be5c374 100644
--- a/quic/tools/quic_memory_cache_backend_test.cc
+++ b/quic/tools/quic_memory_cache_backend_test.cc
@@ -16,7 +16,7 @@
 namespace {
 typedef QuicBackendResponse Response;
 typedef QuicBackendResponse::ServerPushInfo ServerPushInfo;
-};  // namespace
+}  // namespace
 
 class QuicMemoryCacheBackendTest : public QuicTest {
  protected:
diff --git a/quic/tools/quic_packet_printer_bin.cc b/quic/tools/quic_packet_printer_bin.cc
index 2900ce8..3dd7493 100644
--- a/quic/tools/quic_packet_printer_bin.cc
+++ b/quic/tools/quic_packet_printer_bin.cc
@@ -80,6 +80,9 @@
     std::cerr << "OnPacketHeader\n";
     return true;
   }
+  void OnCoalescedPacket(const QuicEncryptedPacket& packet) override {
+    std::cerr << "OnCoalescedPacket\n";
+  }
   bool OnStreamFrame(const QuicStreamFrame& frame) override {
     std::cerr << "OnStreamFrame: " << frame;
     std::cerr << "         data: { "
diff --git a/quic/tools/quic_server.cc b/quic/tools/quic_server.cc
index eaec5d4..006098c 100644
--- a/quic/tools/quic_server.cc
+++ b/quic/tools/quic_server.cc
@@ -147,7 +147,7 @@
 QuicDispatcher* QuicServer::CreateQuicDispatcher() {
   QuicEpollAlarmFactory alarm_factory(&epoll_server_);
   return new QuicSimpleDispatcher(
-      config_, &crypto_config_, &version_manager_,
+      &config_, &crypto_config_, &version_manager_,
       std::unique_ptr<QuicEpollConnectionHelper>(new QuicEpollConnectionHelper(
           &epoll_server_, QuicAllocator::BUFFER_POOL)),
       std::unique_ptr<QuicCryptoServerStream::Helper>(
diff --git a/quic/tools/quic_server_bin.cc b/quic/tools/quic_server_bin.cc
index c4c85d3..446d55e 100644
--- a/quic/tools/quic_server_bin.cc
+++ b/quic/tools/quic_server_bin.cc
@@ -5,6 +5,8 @@
 // A binary wrapper for QuicServer.  It listens forever on --port
 // (default 6121) until it's killed or ctrl-cd to death.
 
+#include <vector>
+
 #include "base/commandlineflags.h"
 #include "base/init_google.h"
 #include "net/httpsconnection/certificates.proto.h"
@@ -15,17 +17,28 @@
 #include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h"
 #include "net/third_party/quiche/src/quic/tools/quic_server.h"
 
-DEFINE_int32(port, 6121, "The port the quic server will listen on.");
-DEFINE_string(
+DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t,
+                              port,
+                              6121,
+                              "The port the quic server will listen on.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+    string,
     certificate_dir,
     "/google/src/head/depot/google3/net/third_party/quiche/src/quic/core/crypto/testdata",
     "The directory containing certificate files.");
-DEFINE_string(intermediate_certificate_name,
-              "intermediate.crt",
-              "The name of the file containing the intermediate certificate.");
-DEFINE_string(leaf_certificate_name,
-              "test.example.com",
-              "The name of the file containing the leaf certificate.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+    string,
+    intermediate_certificate_name,
+    "intermediate.crt",
+    "The name of the file containing the intermediate certificate.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+    string,
+    leaf_certificate_name,
+    "test.example.com",
+    "The name of the file containing the leaf certificate.");
 
 std::unique_ptr<quic::ProofSource> CreateProofSource(
     const string& base_directory,
@@ -49,20 +62,28 @@
 }
 
 int main(int argc, char* argv[]) {
-  InitGoogle(argv[0], &argc, &argv, true);
-
-  quic::QuicMemoryCacheBackend memory_cache_backend;
-  if (!FLAGS_quic_response_cache_dir.empty()) {
-    memory_cache_backend.InitializeBackend(FLAGS_quic_response_cache_dir);
+  const char* usage = "Usage: quic_server [options]";
+  std::vector<quic::QuicString> non_option_args =
+      quic::QuicParseCommandLineFlags(usage, argc, argv);
+  if (!non_option_args.empty()) {
+    quic::QuicPrintCommandLineFlagHelp(usage);
+    exit(0);
   }
 
-  quic::QuicServer server(CreateProofSource(FLAGS_certificate_dir,
-                                            FLAGS_intermediate_certificate_name,
-                                            FLAGS_leaf_certificate_name),
-                          &memory_cache_backend);
+  quic::QuicMemoryCacheBackend memory_cache_backend;
+  if (!GetQuicFlag(FLAGS_quic_response_cache_dir).empty()) {
+    memory_cache_backend.InitializeBackend(
+        GetQuicFlag(FLAGS_quic_response_cache_dir));
+  }
 
-  if (!server.CreateUDPSocketAndListen(
-          quic::QuicSocketAddress(quic::QuicIpAddress::Any6(), FLAGS_port))) {
+  quic::QuicServer server(
+      CreateProofSource(GetQuicFlag(FLAGS_certificate_dir),
+                        GetQuicFlag(FLAGS_intermediate_certificate_name),
+                        GetQuicFlag(FLAGS_leaf_certificate_name)),
+      &memory_cache_backend);
+
+  if (!server.CreateUDPSocketAndListen(quic::QuicSocketAddress(
+          quic::QuicIpAddress::Any6(), GetQuicFlag(FLAGS_port)))) {
     return 1;
   }
 
diff --git a/quic/tools/quic_server_test.cc b/quic/tools/quic_server_test.cc
index f549ad4..7b5cd58 100644
--- a/quic/tools/quic_server_test.cc
+++ b/quic/tools/quic_server_test.cc
@@ -7,7 +7,6 @@
 #include <string.h>
 #include <sys/socket.h>
 
-#include "net/util/netutil.h"
 #include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
 #include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h"
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
@@ -15,6 +14,7 @@
 #include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_port_utils.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h"
@@ -35,7 +35,7 @@
 class QuicServerTest : public QuicTest {};
 
 TEST_F(QuicServerTest, TestDroppedPackets) {
-  int port = net_util::PickUnusedPortOrDie();
+  int port = QuicPickUnusedPortOrDie();
 
   QuicSocketAddress server_address(TestLoopback(), port);
   QuicMemoryCacheBackend response_cache;
@@ -78,7 +78,7 @@
 class MockQuicSimpleDispatcher : public QuicSimpleDispatcher {
  public:
   MockQuicSimpleDispatcher(
-      const QuicConfig& config,
+      const QuicConfig* config,
       const QuicCryptoServerConfig* crypto_config,
       QuicVersionManager* version_manager,
       std::unique_ptr<QuicConnectionHelperInterface> helper,
@@ -113,7 +113,7 @@
  protected:
   QuicDispatcher* CreateQuicDispatcher() override {
     mock_dispatcher_ = new MockQuicSimpleDispatcher(
-        config(), &crypto_config(), version_manager(),
+        &config(), &crypto_config(), version_manager(),
         std::unique_ptr<QuicEpollConnectionHelper>(
             new QuicEpollConnectionHelper(epoll_server(),
                                           QuicAllocator::BUFFER_POOL)),
@@ -132,7 +132,7 @@
 class QuicServerEpollInTest : public QuicTest {
  public:
   QuicServerEpollInTest()
-      : port_(net_util::PickUnusedPortOrDie()),
+      : port_(QuicPickUnusedPortOrDie()),
         server_address_(TestLoopback(), port_) {}
 
   void StartListening() {
@@ -203,7 +203,7 @@
                        TlsServerHandshaker::CreateSslCtx()),
         version_manager_(AllSupportedVersions()),
         dispatcher_(
-            config_,
+            &config_,
             &crypto_config_,
             &version_manager_,
             std::unique_ptr<QuicEpollConnectionHelper>(
diff --git a/quic/tools/quic_simple_crypto_server_stream_helper.cc b/quic/tools/quic_simple_crypto_server_stream_helper.cc
index 567c670..bdd6731 100644
--- a/quic/tools/quic_simple_crypto_server_stream_helper.cc
+++ b/quic/tools/quic_simple_crypto_server_stream_helper.cc
@@ -19,7 +19,7 @@
 QuicSimpleCryptoServerStreamHelper::GenerateConnectionIdForReject(
     QuicTransportVersion /*version*/,
     QuicConnectionId /*connection_id*/) const {
-  return QuicUtils::CreateRandomConnectionId(random_, Perspective::IS_SERVER);
+  return QuicUtils::CreateRandomConnectionId(random_);
 }
 
 bool QuicSimpleCryptoServerStreamHelper::CanAcceptClientHello(
@@ -27,7 +27,7 @@
     const QuicSocketAddress& client_address,
     const QuicSocketAddress& peer_address,
     const QuicSocketAddress& self_address,
-    string* error_details) const {
+    QuicString* error_details) const {
   return true;
 }
 
diff --git a/quic/tools/quic_simple_crypto_server_stream_helper_test.cc b/quic/tools/quic_simple_crypto_server_stream_helper_test.cc
index 652f282..4ede588 100644
--- a/quic/tools/quic_simple_crypto_server_stream_helper_test.cc
+++ b/quic/tools/quic_simple_crypto_server_stream_helper_test.cc
@@ -1,3 +1,7 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
 #include "net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h"
 
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
@@ -14,10 +18,6 @@
   QuicSimpleCryptoServerStreamHelper helper(&random);
 
   EXPECT_EQ(QuicUtils::CreateRandomConnectionId(&random),
-            helper.GenerateConnectionIdForReject(QUIC_VERSION_35,
-                                                 test::TestConnectionId()));
-
-  EXPECT_EQ(QuicUtils::CreateRandomConnectionId(&random),
             helper.GenerateConnectionIdForReject(QUIC_VERSION_99,
                                                  test::TestConnectionId()));
 }
diff --git a/quic/tools/quic_simple_dispatcher.cc b/quic/tools/quic_simple_dispatcher.cc
index 166dd0b..942eb0a 100644
--- a/quic/tools/quic_simple_dispatcher.cc
+++ b/quic/tools/quic_simple_dispatcher.cc
@@ -9,7 +9,7 @@
 namespace quic {
 
 QuicSimpleDispatcher::QuicSimpleDispatcher(
-    const QuicConfig& config,
+    const QuicConfig* config,
     const QuicCryptoServerConfig* crypto_config,
     QuicVersionManager* version_manager,
     std::unique_ptr<QuicConnectionHelperInterface> helper,
diff --git a/quic/tools/quic_simple_dispatcher.h b/quic/tools/quic_simple_dispatcher.h
index 3ee8c0e..6f13d39 100644
--- a/quic/tools/quic_simple_dispatcher.h
+++ b/quic/tools/quic_simple_dispatcher.h
@@ -14,7 +14,7 @@
 class QuicSimpleDispatcher : public QuicDispatcher {
  public:
   QuicSimpleDispatcher(
-      const QuicConfig& config,
+      const QuicConfig* config,
       const QuicCryptoServerConfig* crypto_config,
       QuicVersionManager* version_manager,
       std::unique_ptr<QuicConnectionHelperInterface> helper,
diff --git a/quic/tools/quic_simple_server_session.h b/quic/tools/quic_simple_server_session.h
index d459da6..45c97d0 100644
--- a/quic/tools/quic_simple_server_session.h
+++ b/quic/tools/quic_simple_server_session.h
@@ -7,7 +7,10 @@
 #ifndef QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_SESSION_H_
 #define QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_SESSION_H_
 
+#include <stdint.h>
+
 #include <list>
+#include <memory>
 #include <set>
 #include <string>
 #include <utility>
diff --git a/quic/tools/quic_simple_server_session_test.cc b/quic/tools/quic_simple_server_session_test.cc
index 1ba330d..832a611 100644
--- a/quic/tools/quic_simple_server_session_test.cc
+++ b/quic/tools/quic_simple_server_session_test.cc
@@ -40,6 +40,7 @@
 using testing::_;
 using testing::AtLeast;
 using testing::InSequence;
+using testing::Invoke;
 using testing::Return;
 using testing::StrictMock;
 
@@ -119,7 +120,15 @@
       : MockQuicConnection(helper,
                            alarm_factory,
                            perspective,
-                           supported_versions) {}
+                           supported_versions) {
+    auto consume_all_data = [](QuicStreamId id, size_t write_length,
+                               QuicStreamOffset offset,
+                               StreamSendingState state) {
+      return QuicConsumedData(write_length, state != NO_FIN);
+    };
+    ON_CALL(*this, SendStreamData(_, _, _, _))
+        .WillByDefault(Invoke(consume_all_data));
+  }
 
   MOCK_METHOD4(SendStreamData,
                QuicConsumedData(QuicStreamId id,
@@ -159,22 +168,6 @@
                       QuicStreamId promised_stream_id,
                       const spdy::SpdyHeaderBlock& headers));
 
-  size_t WriteHeaders(QuicStreamId stream_id,
-                      spdy::SpdyHeaderBlock headers,
-                      bool fin,
-                      spdy::SpdyPriority priority,
-                      QuicReferenceCountedPointer<QuicAckListenerInterface>
-                          ack_listener) override {
-    return WriteHeadersMock(stream_id, headers, fin, priority, ack_listener);
-  }
-  MOCK_METHOD5(
-      WriteHeadersMock,
-      size_t(QuicStreamId stream_id,
-             const spdy::SpdyHeaderBlock& headers,
-             bool fin,
-             spdy::SpdyPriority priority,
-             const QuicReferenceCountedPointer<QuicAckListenerInterface>&
-                 ack_listener));
   MOCK_METHOD1(SendBlocked, void(QuicStreamId));
 };
 
@@ -232,7 +225,7 @@
         ->OnSuccessfulVersionNegotiation(supported_versions.front());
     visitor_ = QuicConnectionPeer::GetVisitor(connection_);
 
-    if (connection_->transport_version() == QUIC_VERSION_99) {
+    if (IsVersion99()) {
       EXPECT_CALL(*connection_, SendControlFrame(_))
           .WillRepeatedly(Invoke(
               this,
@@ -242,13 +235,17 @@
   }
 
   QuicStreamId GetNthClientInitiatedBidirectionalId(int n) {
-    return QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
-        *session_, n);
+    return GetNthClientInitiatedBidirectionalStreamId(
+        connection_->transport_version(), n);
   }
 
   QuicStreamId GetNthServerInitiatedUnidirectionalId(int n) {
-    return QuicSpdySessionPeer::GetNthServerInitiatedUnidirectionalStreamId(
-        *session_, n);
+    return quic::test::GetNthServerInitiatedUnidirectionalStreamId(
+        connection_->transport_version(), n);
+  }
+
+  bool IsVersion99() const {
+    return connection_->transport_version() == QUIC_VERSION_99;
   }
 
   void InjectStopSending(QuicStreamId stream_id,
@@ -256,7 +253,7 @@
     // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a
     // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes
     // a one-way close.
-    if (connection_->transport_version() != QUIC_VERSION_99) {
+    if (!IsVersion99()) {
       // Only needed for version 99/IETF QUIC.
       return;
     }
@@ -284,9 +281,9 @@
   QuicConnectionVisitorInterface* visitor_;
 };
 
-INSTANTIATE_TEST_CASE_P(Tests,
-                        QuicSimpleServerSessionTest,
-                        ::testing::ValuesIn(AllSupportedVersions()));
+INSTANTIATE_TEST_SUITE_P(Tests,
+                         QuicSimpleServerSessionTest,
+                         ::testing::ValuesIn(AllSupportedVersions()));
 
 TEST_P(QuicSimpleServerSessionTest, CloseStreamDueToReset) {
   // Open a stream, then reset it.
@@ -302,9 +299,12 @@
                           QUIC_ERROR_PROCESSING_STREAM, 0);
   EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
   EXPECT_CALL(*connection_, SendControlFrame(_));
-  EXPECT_CALL(*connection_,
-              OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
-                            QUIC_RST_ACKNOWLEDGEMENT));
+  if (!IsVersion99()) {
+    // For version 99, this is covered in InjectStopSending()
+    EXPECT_CALL(*connection_,
+                OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
+                              QUIC_RST_ACKNOWLEDGEMENT));
+  }
   visitor_->OnRstStream(rst1);
   // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a
   // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes
@@ -327,12 +327,13 @@
                           GetNthClientInitiatedBidirectionalId(0),
                           QUIC_ERROR_PROCESSING_STREAM, 0);
   EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
-  if (connection_->transport_version() != QUIC_VERSION_99) {
+  if (!IsVersion99()) {
     EXPECT_CALL(*connection_, SendControlFrame(_));
+    // For version 99, this is covered in InjectStopSending()
+    EXPECT_CALL(*connection_,
+                OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
+                              QUIC_RST_ACKNOWLEDGEMENT));
   }
-  EXPECT_CALL(*connection_,
-              OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
-                            QUIC_RST_ACKNOWLEDGEMENT));
   visitor_->OnRstStream(rst1);
   // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a
   // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes
@@ -367,12 +368,13 @@
                          GetNthClientInitiatedBidirectionalId(0),
                          QUIC_ERROR_PROCESSING_STREAM, 0);
   EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
-  if (connection_->transport_version() != QUIC_VERSION_99) {
+  if (!IsVersion99()) {
     EXPECT_CALL(*connection_, SendControlFrame(_));
+    // For version 99, this is covered in InjectStopSending()
+    EXPECT_CALL(*connection_,
+                OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
+                              QUIC_RST_ACKNOWLEDGEMENT));
   }
-  EXPECT_CALL(*connection_,
-              OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
-                            QUIC_RST_ACKNOWLEDGEMENT));
   visitor_->OnRstStream(rst);
   // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a
   // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes
@@ -541,7 +543,7 @@
  protected:
   const size_t kStreamFlowControlWindowSize = 32 * 1024;  // 32KB.
 
-  QuicSimpleServerSessionServerPushTest() : QuicSimpleServerSessionTest() {
+  QuicSimpleServerSessionServerPushTest() {
     // Reset stream level flow control window to be 32KB.
     QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(
         &config_, kStreamFlowControlWindowSize);
@@ -550,10 +552,6 @@
     // control blocks it.
     QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(
         &config_, kInitialSessionFlowControlWindowForTest);
-    // Enable server push.
-    QuicTagVector copt;
-    copt.push_back(kSPSH);
-    QuicConfigPeer::SetReceivedConnectionOptions(&config_, copt);
 
     ParsedQuicVersionVector supported_versions = SupportedVersions(GetParam());
     connection_ = new StrictMock<MockQuicConnectionWithSendStreamData>(
@@ -566,7 +564,7 @@
         ->OnSuccessfulVersionNegotiation(supported_versions.front());
     // Needed to make new session flow control window and server push work.
 
-    if (connection_->transport_version() == QUIC_VERSION_99) {
+    if (IsVersion99()) {
       EXPECT_CALL(*connection_, SendControlFrame(_))
           .WillRepeatedly(Invoke(this, &QuicSimpleServerSessionServerPushTest::
                                            ClearMaxStreamIdControlFrame));
@@ -619,7 +617,7 @@
       QuicString body(body_size, 'a');
       QuicString data;
       header_length = 0;
-      if (connection_->transport_version() == QUIC_VERSION_99) {
+      if (VersionHasDataFrameHeader(connection_->transport_version())) {
         HttpEncoder encoder;
         std::unique_ptr<char[]> buffer;
         header_length =
@@ -640,14 +638,11 @@
                                        stream_id, _));
       if (i <= kMaxStreamsForTest) {
         // |kMaxStreamsForTest| promised responses should be sent.
-        EXPECT_CALL(*session_,
-                    WriteHeadersMock(stream_id, _, false,
-                                     QuicStream::kDefaultPriority, _));
         // Since flow control window is smaller than response body, not the
         // whole body will be sent.
-        if (connection_->transport_version() == QUIC_VERSION_99) {
-          EXPECT_CALL(*connection_, SendStreamData(stream_id, _, 0, NO_FIN))
-              .WillOnce(Return(QuicConsumedData(header_length, false)));
+        if (VersionHasDataFrameHeader(connection_->transport_version())) {
+          EXPECT_CALL(*connection_,
+                      SendStreamData(stream_id, header_length, 0, NO_FIN));
         }
         EXPECT_CALL(*connection_,
                     SendStreamData(stream_id, _, header_length, NO_FIN))
@@ -661,26 +656,34 @@
                                    request_headers);
     return header_length;
   }
+
+  void ConsumeHeadersStreamData() {
+    QuicStreamId headers_stream_id =
+        QuicUtils::GetHeadersStreamId(connection_->transport_version());
+    EXPECT_CALL(*connection_, SendStreamData(headers_stream_id, _, _, _))
+        .Times(AtLeast(1));
+  }
 };
 
-INSTANTIATE_TEST_CASE_P(Tests,
-                        QuicSimpleServerSessionServerPushTest,
-                        ::testing::ValuesIn(AllSupportedVersions()));
+INSTANTIATE_TEST_SUITE_P(Tests,
+                         QuicSimpleServerSessionServerPushTest,
+                         ::testing::ValuesIn(AllSupportedVersions()));
 
+// Tests that given more than kMaxStreamsForTest resources, all their
+// PUSH_PROMISE's will be sent out and only kMaxStreamsForTest streams will be
+// opened and send push response.
 TEST_P(QuicSimpleServerSessionServerPushTest, TestPromisePushResources) {
-  // Tests that given more than kMaxOpenStreamForTest resources, all their
-  // PUSH_PROMISE's will be sent out and only |kMaxOpenStreamForTest| streams
-  // will be opened and send push response.
-
+  ConsumeHeadersStreamData();
   size_t num_resources = kMaxStreamsForTest + 5;
   PromisePushResources(num_resources);
   EXPECT_EQ(kMaxStreamsForTest, session_->GetNumOpenOutgoingStreams());
 }
 
+// Tests that after promised stream queued up, when an opened stream is marked
+// draining, a queued promised stream will become open and send push response.
 TEST_P(QuicSimpleServerSessionServerPushTest,
        HandlePromisedPushRequestsAfterStreamDraining) {
-  // Tests that after promised stream queued up, when an opened stream is marked
-  // draining, a queued promised stream will become open and send push response.
+  ConsumeHeadersStreamData();
   size_t num_resources = kMaxStreamsForTest + 1;
   QuicByteCount header_length = PromisePushResources(num_resources);
   QuicStreamId next_out_going_stream_id =
@@ -688,12 +691,9 @@
 
   // After an open stream is marked draining, a new stream is expected to be
   // created and a response sent on the stream.
-  EXPECT_CALL(*session_, WriteHeadersMock(next_out_going_stream_id, _, false,
-                                          QuicStream::kDefaultPriority, _));
-  if (connection_->transport_version() == QUIC_VERSION_99) {
-    EXPECT_CALL(*connection_,
-                SendStreamData(next_out_going_stream_id, _, 0, NO_FIN))
-        .WillOnce(Return(QuicConsumedData(header_length, false)));
+  if (VersionHasDataFrameHeader(connection_->transport_version())) {
+    EXPECT_CALL(*connection_, SendStreamData(next_out_going_stream_id,
+                                             header_length, 0, NO_FIN));
   }
   EXPECT_CALL(*connection_, SendStreamData(next_out_going_stream_id, _,
                                            header_length, NO_FIN))
@@ -701,7 +701,7 @@
           kStreamFlowControlWindowSize - header_length, false)));
   EXPECT_CALL(*session_, SendBlocked(next_out_going_stream_id));
 
-  if (connection_->transport_version() == QUIC_VERSION_99) {
+  if (IsVersion99()) {
     // The PromisePushedResources call, above, will have used all available
     // stream ids.  For version 99, stream ids are not made available until
     // a MAX_STREAM_ID frame is received. This emulates the reception of one.
@@ -716,15 +716,16 @@
   EXPECT_EQ(kMaxStreamsForTest, session_->GetNumOpenOutgoingStreams());
 }
 
+// Tests that after all resources are promised, a RST frame from client can
+// prevent a promised resource to be send out.
 TEST_P(QuicSimpleServerSessionServerPushTest,
        ResetPromisedStreamToCancelServerPush) {
-  // Tests that after all resources are promised, a RST frame from client can
-  // prevent a promised resource to be send out.
+  ConsumeHeadersStreamData();
 
   // Having two extra resources to be send later. One of them will be reset, so
   // when opened stream become close, only one will become open.
   size_t num_resources = kMaxStreamsForTest + 2;
-  if (connection_->transport_version() == QUIC_VERSION_99) {
+  if (IsVersion99()) {
     // V99 will send out a stream-id-blocked frame when the we desired to exceed
     // the limit. This will clear the frames so that they do not block the later
     // rst-stream frame.
@@ -753,22 +754,17 @@
   QuicStreamId stream_not_reset =
       GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest);
   InSequence s;
-  EXPECT_CALL(*session_, WriteHeadersMock(stream_not_reset, _, false,
-                                          QuicStream::kDefaultPriority, _));
-  if (connection_->transport_version() == QUIC_VERSION_99) {
-    EXPECT_CALL(*connection_, SendStreamData(stream_not_reset, _, 0, NO_FIN))
-        .WillOnce(Return(QuicConsumedData(header_length, false)));
+  if (VersionHasDataFrameHeader(connection_->transport_version())) {
+    EXPECT_CALL(*connection_,
+                SendStreamData(stream_not_reset, header_length, 0, NO_FIN));
   }
   EXPECT_CALL(*connection_,
               SendStreamData(stream_not_reset, _, header_length, NO_FIN))
       .WillOnce(Return(QuicConsumedData(
           kStreamFlowControlWindowSize - header_length, false)));
   EXPECT_CALL(*session_, SendBlocked(stream_not_reset));
-  EXPECT_CALL(*session_, WriteHeadersMock(stream_got_reset, _, false,
-                                          QuicStream::kDefaultPriority, _))
-      .Times(0);
 
-  if (connection_->transport_version() == QUIC_VERSION_99) {
+  if (IsVersion99()) {
     // The PromisePushedResources call, above, will have used all available
     // stream ids.  For version 99, stream ids are not made available until
     // a MAX_STREAM_ID frame is received. This emulates the reception of one.
@@ -781,12 +777,13 @@
   session_->StreamDraining(GetNthServerInitiatedUnidirectionalId(1));
 }
 
+// Tests that closing a open outgoing stream can trigger a promised resource in
+// the queue to be send out.
 TEST_P(QuicSimpleServerSessionServerPushTest,
        CloseStreamToHandleMorePromisedStream) {
-  // Tests that closing a open outgoing stream can trigger a promised resource
-  // in the queue to be send out.
+  ConsumeHeadersStreamData();
   size_t num_resources = kMaxStreamsForTest + 1;
-  if (connection_->transport_version() == QUIC_VERSION_99) {
+  if (IsVersion99()) {
     // V99 will send out a stream-id-blocked frame when the we desired to exceed
     // the limit. This will clear the frames so that they do not block the later
     // rst-stream frame.
@@ -803,13 +800,14 @@
   QuicStreamId stream_got_reset = GetNthServerInitiatedUnidirectionalId(0);
   EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
   EXPECT_CALL(*connection_, SendControlFrame(_));
-  EXPECT_CALL(*connection_,
-              OnStreamReset(stream_got_reset, QUIC_RST_ACKNOWLEDGEMENT));
-  EXPECT_CALL(*session_, WriteHeadersMock(stream_to_open, _, false,
-                                          QuicStream::kDefaultPriority, _));
-  if (connection_->transport_version() == QUIC_VERSION_99) {
-    EXPECT_CALL(*connection_, SendStreamData(stream_to_open, _, 0, NO_FIN))
-        .WillOnce(Return(QuicConsumedData(header_length, false)));
+  if (!IsVersion99()) {
+    // For version 99, this is covered in InjectStopSending()
+    EXPECT_CALL(*connection_,
+                OnStreamReset(stream_got_reset, QUIC_RST_ACKNOWLEDGEMENT));
+  }
+  if (VersionHasDataFrameHeader(connection_->transport_version())) {
+    EXPECT_CALL(*connection_,
+                SendStreamData(stream_to_open, header_length, 0, NO_FIN));
   }
   EXPECT_CALL(*connection_,
               SendStreamData(stream_to_open, _, header_length, NO_FIN))
@@ -819,7 +817,7 @@
   EXPECT_CALL(*session_, SendBlocked(stream_to_open));
   QuicRstStreamFrame rst(kInvalidControlFrameId, stream_got_reset,
                          QUIC_STREAM_CANCELLED, 0);
-  if (connection_->transport_version() == QUIC_VERSION_99) {
+  if (IsVersion99()) {
     // The PromisePushedResources call, above, will have used all available
     // stream ids.  For version 99, stream ids are not made available until
     // a MAX_STREAM_ID frame is received. This emulates the reception of one.
diff --git a/quic/tools/quic_simple_server_stream.cc b/quic/tools/quic_simple_server_stream.cc
index 1f70a8a..2ae2dbf 100644
--- a/quic/tools/quic_simple_server_stream.cc
+++ b/quic/tools/quic_simple_server_stream.cc
@@ -291,7 +291,7 @@
   QUIC_DLOG(INFO) << "Stream " << id()
                   << " writing body (fin = false) with size: " << body.size();
   if (!body.empty()) {
-    WriteOrBufferBody(body, /*fin=*/false, nullptr);
+    WriteOrBufferBody(body, /*fin=*/false);
   }
 }
 
@@ -321,7 +321,7 @@
   QUIC_DLOG(INFO) << "Stream " << id() << " writing body (fin = " << send_fin
                   << ") with size: " << body.size();
   if (!body.empty() || send_fin) {
-    WriteOrBufferBody(body, send_fin, nullptr);
+    WriteOrBufferBody(body, send_fin);
   }
   if (send_fin) {
     // Nothing else to send.
diff --git a/quic/tools/quic_simple_server_stream.h b/quic/tools/quic_simple_server_stream.h
index 8939a2c..2e6826f 100644
--- a/quic/tools/quic_simple_server_stream.h
+++ b/quic/tools/quic_simple_server_stream.h
@@ -15,10 +15,6 @@
 
 namespace quic {
 
-namespace test {
-class QuicSimpleServerStreamPeer;
-}  // namespace test
-
 // All this does right now is aggregate data, and on fin, send an HTTP
 // response.
 class QuicSimpleServerStream : public QuicSpdyServerStreamBase,
@@ -93,14 +89,12 @@
 
   const QuicString& body() { return body_; }
 
- private:
-  friend class test::QuicSimpleServerStreamPeer;
-
   // The parsed headers received from the client.
   spdy::SpdyHeaderBlock request_headers_;
   int64_t content_length_;
   QuicString body_;
 
+ private:
   QuicSimpleServerBackend* quic_simple_server_backend_;  // Not owned.
 };
 
diff --git a/quic/tools/quic_simple_server_stream_test.cc b/quic/tools/quic_simple_server_stream_test.cc
index 4e9e68d..a13c9a2 100644
--- a/quic/tools/quic_simple_server_stream_test.cc
+++ b/quic/tools/quic_simple_server_stream_test.cc
@@ -32,50 +32,51 @@
 using testing::Invoke;
 using testing::Return;
 using testing::StrictMock;
+using testing::ValuesIn;
 
 namespace quic {
 namespace test {
 
-size_t kFakeFrameLen = 60;
+const size_t kFakeFrameLen = 60;
+const size_t kErrorLength = strlen(QuicSimpleServerStream::kErrorResponseBody);
+const size_t kDataFrameHeaderLength = 2;
 
-class QuicSimpleServerStreamPeer : public QuicSimpleServerStream {
+class TestStream : public QuicSimpleServerStream {
  public:
-  QuicSimpleServerStreamPeer(
-      QuicStreamId stream_id,
-      QuicSpdySession* session,
-      StreamType type,
-      QuicSimpleServerBackend* quic_simple_server_backend)
+  TestStream(QuicStreamId stream_id,
+             QuicSpdySession* session,
+             StreamType type,
+             QuicSimpleServerBackend* quic_simple_server_backend)
       : QuicSimpleServerStream(stream_id,
                                session,
                                type,
                                quic_simple_server_backend) {}
 
-  ~QuicSimpleServerStreamPeer() override = default;
+  ~TestStream() override = default;
 
-  using QuicSimpleServerStream::SendErrorResponse;
-  using QuicSimpleServerStream::SendResponse;
+  MOCK_METHOD1(WriteHeadersMock, void(bool fin));
+
+  size_t WriteHeaders(spdy::SpdyHeaderBlock header_block,
+                      bool fin,
+                      QuicReferenceCountedPointer<QuicAckListenerInterface>
+                          ack_listener) override {
+    WriteHeadersMock(fin);
+    return 0;
+  }
+
+  // Expose protected QuicSimpleServerStream methods.
+  void DoSendResponse() { SendResponse(); }
+  void DoSendErrorResponse() { SendErrorResponse(); }
 
   spdy::SpdyHeaderBlock* mutable_headers() { return &request_headers_; }
   void set_body(QuicString body) { body_ = std::move(body); }
+  const QuicString& body() const { return body_; }
+  int content_length() const { return content_length_; }
 
-  static void SendResponse(QuicSimpleServerStream* stream) {
-    stream->SendResponse();
-  }
-
-  static void SendErrorResponse(QuicSimpleServerStream* stream) {
-    stream->SendErrorResponse();
-  }
-
-  static const QuicString& body(QuicSimpleServerStream* stream) {
-    return stream->body_;
-  }
-
-  static int content_length(QuicSimpleServerStream* stream) {
-    return stream->content_length_;
-  }
-
-  static spdy::SpdyHeaderBlock& headers(QuicSimpleServerStream* stream) {
-    return stream->request_headers_;
+  QuicStringPiece GetHeader(QuicStringPiece key) const {
+    auto it = request_headers_.find(key);
+    DCHECK(it != request_headers_.end());
+    return it->second;
   }
 };
 
@@ -85,7 +86,7 @@
  public:
   const size_t kMaxStreamsForTest = 100;
 
-  explicit MockQuicSimpleServerSession(
+  MockQuicSimpleServerSession(
       QuicConnection* connection,
       MockQuicSessionVisitor* owner,
       MockQuicCryptoServerStreamHelper* helper,
@@ -103,7 +104,7 @@
     QuicSessionPeer::SetMaxOpenIncomingStreams(this, kMaxStreamsForTest);
     QuicSessionPeer::SetMaxOpenOutgoingStreams(this, kMaxStreamsForTest);
     ON_CALL(*this, WritevData(_, _, _, _, _))
-        .WillByDefault(testing::Return(QuicConsumedData(0, false)));
+        .WillByDefault(Invoke(MockQuicSession::ConsumeData));
   }
 
   MockQuicSimpleServerSession(const MockQuicSimpleServerSession&) = delete;
@@ -129,24 +130,6 @@
                     const QuicHeaderList& header_list));
   MOCK_METHOD2(OnStreamHeadersPriority,
                void(QuicStreamId stream_id, spdy::SpdyPriority priority));
-  // Methods taking non-copyable types like SpdyHeaderBlock by value cannot be
-  // mocked directly.
-  size_t WriteHeaders(QuicStreamId id,
-                      spdy::SpdyHeaderBlock headers,
-                      bool fin,
-                      spdy::SpdyPriority priority,
-                      QuicReferenceCountedPointer<QuicAckListenerInterface>
-                          ack_listener) override {
-    return WriteHeadersMock(id, headers, fin, priority, ack_listener);
-  }
-  MOCK_METHOD5(
-      WriteHeadersMock,
-      size_t(QuicStreamId id,
-             const spdy::SpdyHeaderBlock& headers,
-             bool fin,
-             spdy::SpdyPriority priority,
-             const QuicReferenceCountedPointer<QuicAckListenerInterface>&
-                 ack_listener));
   MOCK_METHOD3(SendRstStream,
                void(QuicStreamId stream_id,
                     QuicRstStreamErrorCode error,
@@ -198,8 +181,7 @@
                  &compressed_certs_cache_,
                  &memory_cache_backend_),
         quic_response_(new QuicBackendResponse),
-        body_("hello world"),
-        is_version_99_(connection_->transport_version() == QUIC_VERSION_99) {
+        body_("hello world") {
     connection_->set_visitor(&session_);
     header_list_.OnHeaderBlockStart();
     header_list_.OnHeader(":authority", "www.google.com");
@@ -216,23 +198,29 @@
         kInitialStreamFlowControlWindowForTest);
     session_.config()->SetInitialSessionFlowControlWindowToSend(
         kInitialSessionFlowControlWindowForTest);
-    stream_ = new QuicSimpleServerStreamPeer(
-        QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
-            session_, 0),
+    stream_ = new StrictMock<TestStream>(
+        GetNthClientInitiatedBidirectionalStreamId(
+            connection_->transport_version(), 0),
         &session_, BIDIRECTIONAL, &memory_cache_backend_);
     // Register stream_ in dynamic_stream_map_ and pass ownership to session_.
     session_.ActivateStream(QuicWrapUnique(stream_));
     connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
   }
 
-  const QuicString& StreamBody() {
-    return QuicSimpleServerStreamPeer::body(stream_);
-  }
+  const QuicString& StreamBody() { return stream_->body(); }
 
   QuicString StreamHeadersValue(const QuicString& key) {
     return (*stream_->mutable_headers())[key].as_string();
   }
 
+  bool IsVersion99() const {
+    return connection_->transport_version() == QUIC_VERSION_99;
+  }
+
+  bool HasFrameHeader() const {
+    return VersionHasDataFrameHeader(connection_->transport_version());
+  }
+
   spdy::SpdyHeaderBlock response_headers_;
   MockQuicConnectionHelper helper_;
   MockAlarmFactory alarm_factory_;
@@ -243,28 +231,26 @@
   QuicCompressedCertsCache compressed_certs_cache_;
   QuicMemoryCacheBackend memory_cache_backend_;
   StrictMock<MockQuicSimpleServerSession> session_;
-  QuicSimpleServerStreamPeer* stream_;  // Owned by session_.
+  StrictMock<TestStream>* stream_;  // Owned by session_.
   std::unique_ptr<QuicBackendResponse> quic_response_;
   QuicString body_;
   QuicHeaderList header_list_;
   HttpEncoder encoder_;
-  bool is_version_99_;
 };
 
-INSTANTIATE_TEST_CASE_P(Tests,
-                        QuicSimpleServerStreamTest,
-                        ::testing::ValuesIn(AllSupportedVersions()));
+INSTANTIATE_TEST_SUITE_P(Tests,
+                         QuicSimpleServerStreamTest,
+                         ValuesIn(AllSupportedVersions()));
 
 TEST_P(QuicSimpleServerStreamTest, TestFraming) {
   EXPECT_CALL(session_, WritevData(_, _, _, _, _))
-      .Times(AnyNumber())
       .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
   stream_->OnStreamHeaderList(false, kFakeFrameLen, header_list_);
   std::unique_ptr<char[]> buffer;
   QuicByteCount header_length =
       encoder_.SerializeDataFrameHeader(body_.length(), &buffer);
   QuicString header = QuicString(buffer.get(), header_length);
-  QuicString data = is_version_99_ ? header + body_ : body_;
+  QuicString data = HasFrameHeader() ? header + body_ : body_;
   stream_->OnStreamFrame(
       QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
   EXPECT_EQ("11", StreamHeadersValue("content-length"));
@@ -275,7 +261,6 @@
 
 TEST_P(QuicSimpleServerStreamTest, TestFramingOnePacket) {
   EXPECT_CALL(session_, WritevData(_, _, _, _, _))
-      .Times(AnyNumber())
       .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
 
   stream_->OnStreamHeaderList(false, kFakeFrameLen, header_list_);
@@ -283,7 +268,7 @@
   QuicByteCount header_length =
       encoder_.SerializeDataFrameHeader(body_.length(), &buffer);
   QuicString header = QuicString(buffer.get(), header_length);
-  QuicString data = is_version_99_ ? header + body_ : body_;
+  QuicString data = HasFrameHeader() ? header + body_ : body_;
   stream_->OnStreamFrame(
       QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
   EXPECT_EQ("11", StreamHeadersValue("content-length"));
@@ -294,7 +279,6 @@
 
 TEST_P(QuicSimpleServerStreamTest, SendQuicRstStreamNoErrorInStopReading) {
   EXPECT_CALL(session_, WritevData(_, _, _, _, _))
-      .Times(AnyNumber())
       .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
 
   EXPECT_FALSE(stream_->fin_received());
@@ -308,18 +292,16 @@
 }
 
 TEST_P(QuicSimpleServerStreamTest, TestFramingExtraData) {
-  testing::InSequence seq;
+  InSequence seq;
   QuicString large_body = "hello world!!!!!!";
 
   // We'll automatically write out an error (headers + body)
-  EXPECT_CALL(session_, WriteHeadersMock(_, _, _, _, _));
-  if (is_version_99_) {
-    EXPECT_CALL(session_, WritevData(_, _, _, _, _))
-        .WillOnce(Invoke(MockQuicSession::ConsumeData));
+  EXPECT_CALL(*stream_, WriteHeadersMock(false));
+  if (HasFrameHeader()) {
+    EXPECT_CALL(session_, WritevData(_, _, kDataFrameHeaderLength, _, NO_FIN));
   }
+  EXPECT_CALL(session_, WritevData(_, _, kErrorLength, _, FIN));
 
-  EXPECT_CALL(session_, WritevData(_, _, _, _, _))
-      .WillOnce(Invoke(MockQuicSession::ConsumeData));
   EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0);
 
   stream_->OnStreamHeaderList(false, kFakeFrameLen, header_list_);
@@ -327,7 +309,7 @@
   QuicByteCount header_length =
       encoder_.SerializeDataFrameHeader(body_.length(), &buffer);
   QuicString header = QuicString(buffer.get(), header_length);
-  QuicString data = is_version_99_ ? header + body_ : body_;
+  QuicString data = HasFrameHeader() ? header + body_ : body_;
 
   stream_->OnStreamFrame(
       QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
@@ -336,7 +318,7 @@
   header_length =
       encoder_.SerializeDataFrameHeader(large_body.length(), &buffer);
   header = QuicString(buffer.get(), header_length);
-  QuicString data2 = is_version_99_ ? header + large_body : large_body;
+  QuicString data2 = HasFrameHeader() ? header + large_body : large_body;
   stream_->OnStreamFrame(
       QuicStreamFrame(stream_->id(), /*fin=*/true, data.size(), data2));
   EXPECT_EQ("11", StreamHeadersValue("content-length"));
@@ -367,18 +349,13 @@
   stream_->set_fin_received(true);
 
   InSequence s;
-  EXPECT_CALL(session_, WriteHeadersMock(stream_->id(), _, false, _, _));
-  if (is_version_99_) {
-    EXPECT_CALL(session_, WritevData(_, _, _, _, _))
-        .Times(1)
-        .WillOnce(Return(QuicConsumedData(header_length, false)));
+  EXPECT_CALL(*stream_, WriteHeadersMock(false));
+  if (HasFrameHeader()) {
+    EXPECT_CALL(session_, WritevData(_, _, header_length, _, NO_FIN));
   }
-  EXPECT_CALL(session_, WritevData(_, _, _, _, _))
-      .Times(1)
-      .WillOnce(Return(QuicConsumedData(
-          strlen(QuicSimpleServerStream::kErrorResponseBody), true)));
+  EXPECT_CALL(session_, WritevData(_, _, kErrorLength, _, FIN));
 
-  QuicSimpleServerStreamPeer::SendResponse(stream_);
+  stream_->DoSendResponse();
   EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
   EXPECT_TRUE(stream_->write_side_closed());
 }
@@ -407,27 +384,22 @@
   stream_->set_fin_received(true);
 
   InSequence s;
-  EXPECT_CALL(session_, WriteHeadersMock(stream_->id(), _, false, _, _));
-  if (is_version_99_) {
-    EXPECT_CALL(session_, WritevData(_, _, _, _, _))
-        .Times(1)
-        .WillOnce(Return(QuicConsumedData(header_length, false)));
+  EXPECT_CALL(*stream_, WriteHeadersMock(false));
+  if (HasFrameHeader()) {
+    EXPECT_CALL(session_, WritevData(_, _, header_length, _, NO_FIN));
   }
-  EXPECT_CALL(session_, WritevData(_, _, _, _, _))
-      .Times(1)
-      .WillOnce(Return(QuicConsumedData(
-          strlen(QuicSimpleServerStream::kErrorResponseBody), true)));
+  EXPECT_CALL(session_, WritevData(_, _, kErrorLength, _, FIN));
 
-  QuicSimpleServerStreamPeer::SendResponse(stream_);
+  stream_->DoSendResponse();
   EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
   EXPECT_TRUE(stream_->write_side_closed());
 }
 
 TEST_P(QuicSimpleServerStreamTest, SendPushResponseWith404Response) {
   // Create a new promised stream with even id().
-  QuicSimpleServerStreamPeer* promised_stream = new QuicSimpleServerStreamPeer(
-      QuicSpdySessionPeer::GetNthServerInitiatedUnidirectionalStreamId(session_,
-                                                                       0),
+  auto promised_stream = new StrictMock<TestStream>(
+      GetNthServerInitiatedUnidirectionalStreamId(
+          connection_->transport_version(), 0),
       &session_, WRITE_UNIDIRECTIONAL, &memory_cache_backend_);
   session_.ActivateStream(QuicWrapUnique(promised_stream));
 
@@ -451,7 +423,7 @@
   EXPECT_CALL(session_,
               SendRstStream(promised_stream->id(), QUIC_STREAM_CANCELLED, 0));
 
-  QuicSimpleServerStreamPeer::SendResponse(promised_stream);
+  promised_stream->DoSendResponse();
 }
 
 TEST_P(QuicSimpleServerStreamTest, SendResponseWithValidHeaders) {
@@ -476,17 +448,13 @@
   stream_->set_fin_received(true);
 
   InSequence s;
-  EXPECT_CALL(session_, WriteHeadersMock(stream_->id(), _, false, _, _));
-  if (is_version_99_) {
-    EXPECT_CALL(session_, WritevData(_, _, _, _, _))
-        .Times(1)
-        .WillOnce(Return(QuicConsumedData(header_length, false)));
+  EXPECT_CALL(*stream_, WriteHeadersMock(false));
+  if (HasFrameHeader()) {
+    EXPECT_CALL(session_, WritevData(_, _, header_length, _, NO_FIN));
   }
-  EXPECT_CALL(session_, WritevData(_, _, _, _, _))
-      .Times(1)
-      .WillOnce(Return(QuicConsumedData(body.length(), true)));
+  EXPECT_CALL(session_, WritevData(_, _, body.length(), _, FIN));
 
-  QuicSimpleServerStreamPeer::SendResponse(stream_);
+  stream_->DoSendResponse();
   EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
   EXPECT_TRUE(stream_->write_side_closed());
 }
@@ -518,23 +486,17 @@
 
   stream_->set_fin_received(true);
   InSequence s;
-  EXPECT_CALL(
-      session_,
-      PromisePushResourcesMock(
-          host + request_path, _,
-          QuicSpdySessionPeer::GetNthClientInitiatedBidirectionalStreamId(
-              session_, 0),
-          _));
-  EXPECT_CALL(session_, WriteHeadersMock(stream_->id(), _, false, _, _));
-  if (is_version_99_) {
-    EXPECT_CALL(session_, WritevData(_, _, _, _, _))
-        .Times(1)
-        .WillOnce(Return(QuicConsumedData(header_length, false)));
+  EXPECT_CALL(session_, PromisePushResourcesMock(
+                            host + request_path, _,
+                            GetNthClientInitiatedBidirectionalStreamId(
+                                connection_->transport_version(), 0),
+                            _));
+  EXPECT_CALL(*stream_, WriteHeadersMock(false));
+  if (HasFrameHeader()) {
+    EXPECT_CALL(session_, WritevData(_, _, header_length, _, NO_FIN));
   }
-  EXPECT_CALL(session_, WritevData(_, _, _, _, _))
-      .Times(1)
-      .WillOnce(Return(QuicConsumedData(body.length(), true)));
-  QuicSimpleServerStreamPeer::SendResponse(stream_);
+  EXPECT_CALL(session_, WritevData(_, _, body.length(), _, FIN));
+  stream_->DoSendResponse();
   EXPECT_EQ(*request_headers, session_.original_request_headers_);
 }
 
@@ -557,13 +519,12 @@
 
   // Create a stream with even stream id and test against this stream.
   const QuicStreamId kServerInitiatedStreamId =
-      QuicSpdySessionPeer::GetNthServerInitiatedUnidirectionalStreamId(session_,
-                                                                       0);
+      GetNthServerInitiatedUnidirectionalStreamId(
+          connection_->transport_version(), 0);
   // Create a server initiated stream and pass it to session_.
-  QuicSimpleServerStreamPeer* server_initiated_stream =
-      new QuicSimpleServerStreamPeer(kServerInitiatedStreamId, &session_,
-                                     WRITE_UNIDIRECTIONAL,
-                                     &memory_cache_backend_);
+  auto server_initiated_stream =
+      new StrictMock<TestStream>(kServerInitiatedStreamId, &session_,
+                                 WRITE_UNIDIRECTIONAL, &memory_cache_backend_);
   session_.ActivateStream(QuicWrapUnique(server_initiated_stream));
 
   const QuicString kHost = "www.foo.com";
@@ -586,26 +547,18 @@
 
   // Call PushResponse() should trigger stream to fetch response from cache
   // and send it back.
-  EXPECT_CALL(session_,
-              WriteHeadersMock(kServerInitiatedStreamId, _, false,
-                               server_initiated_stream->priority(), _));
-
   InSequence s;
-  if (is_version_99_) {
-    EXPECT_CALL(session_, WritevData(_, kServerInitiatedStreamId, _, _, _))
-        .Times(1)
-        .WillOnce(Return(QuicConsumedData(header_length, false)));
+  EXPECT_CALL(*server_initiated_stream, WriteHeadersMock(false));
+
+  if (HasFrameHeader()) {
+    EXPECT_CALL(session_, WritevData(_, kServerInitiatedStreamId, header_length,
+                                     _, NO_FIN));
   }
-  EXPECT_CALL(session_, WritevData(_, kServerInitiatedStreamId, _, _, _))
-      .Times(1)
-      .WillOnce(Return(QuicConsumedData(kBody.size(), true)));
+  EXPECT_CALL(session_,
+              WritevData(_, kServerInitiatedStreamId, kBody.size(), _, FIN));
   server_initiated_stream->PushResponse(std::move(headers));
-  EXPECT_EQ(kPath, QuicSimpleServerStreamPeer::headers(
-                       server_initiated_stream)[":path"]
-                       .as_string());
-  EXPECT_EQ("GET", QuicSimpleServerStreamPeer::headers(
-                       server_initiated_stream)[":method"]
-                       .as_string());
+  EXPECT_EQ(kPath, server_initiated_stream->GetHeader(":path"));
+  EXPECT_EQ("GET", server_initiated_stream->GetHeader(":method"));
 }
 
 TEST_P(QuicSimpleServerStreamTest, TestSendErrorResponse) {
@@ -614,17 +567,13 @@
   stream_->set_fin_received(true);
 
   InSequence s;
-  EXPECT_CALL(session_, WriteHeadersMock(_, _, _, _, _));
-  if (is_version_99_) {
-    EXPECT_CALL(session_, WritevData(_, _, _, _, _))
-        .Times(1)
-        .WillOnce(Return(QuicConsumedData(2, false)));
+  EXPECT_CALL(*stream_, WriteHeadersMock(false));
+  if (HasFrameHeader()) {
+    EXPECT_CALL(session_, WritevData(_, _, kDataFrameHeaderLength, _, NO_FIN));
   }
-  EXPECT_CALL(session_, WritevData(_, _, _, _, _))
-      .Times(1)
-      .WillOnce(Return(QuicConsumedData(3, true)));
+  EXPECT_CALL(session_, WritevData(_, _, kErrorLength, _, FIN));
 
-  QuicSimpleServerStreamPeer::SendErrorResponse(stream_);
+  stream_->DoSendErrorResponse();
   EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
   EXPECT_TRUE(stream_->write_side_closed());
 }
@@ -636,9 +585,8 @@
   // \000 is a way to write the null byte when followed by a literal digit.
   header_list_.OnHeader("content-length", QuicStringPiece("11\00012", 5));
 
-  EXPECT_CALL(session_, WriteHeadersMock(_, _, _, _, _));
+  EXPECT_CALL(*stream_, WriteHeadersMock(false));
   EXPECT_CALL(session_, WritevData(_, _, _, _, _))
-      .Times(AnyNumber())
       .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
   stream_->OnStreamHeaderList(true, kFakeFrameLen, header_list_);
 
@@ -654,9 +602,8 @@
   // \000 is a way to write the null byte when followed by a literal digit.
   header_list_.OnHeader("content-length", QuicStringPiece("\00012", 3));
 
-  EXPECT_CALL(session_, WriteHeadersMock(_, _, _, _, _));
+  EXPECT_CALL(*stream_, WriteHeadersMock(false));
   EXPECT_CALL(session_, WritevData(_, _, _, _, _))
-      .Times(AnyNumber())
       .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
   stream_->OnStreamHeaderList(true, kFakeFrameLen, header_list_);
 
@@ -672,7 +619,7 @@
 
   stream_->OnStreamHeaderList(false, kFakeFrameLen, header_list_);
 
-  EXPECT_EQ(11, QuicSimpleServerStreamPeer::content_length(stream_));
+  EXPECT_EQ(11, stream_->content_length());
   EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
   EXPECT_FALSE(stream_->reading_stopped());
   EXPECT_FALSE(stream_->write_side_closed());
@@ -688,7 +635,7 @@
   QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(),
                                QUIC_STREAM_CANCELLED, 1234);
   stream_->OnStreamReset(rst_frame);
-  if (connection_->transport_version() == QUIC_VERSION_99) {
+  if (IsVersion99()) {
     // For V99 receiving a RST_STREAM causes a 1-way close; the test requires
     // a full close. A CloseWriteSide closes the other half of the stream.
     // Everything should then work properly.
diff --git a/quic/tools/quic_tcp_like_trace_converter.h b/quic/tools/quic_tcp_like_trace_converter.h
index 6c9a241..4329805 100644
--- a/quic/tools/quic_tcp_like_trace_converter.h
+++ b/quic/tools/quic_tcp_like_trace_converter.h
@@ -8,9 +8,10 @@
 #include <vector>
 
 #include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval_set.h"
 #include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
-#include "net/third_party/quiche/src/quic/platform/api/quic_interval.h"
 
 namespace quic {
 
diff --git a/spdy/core/hpack/hpack_constants.h b/spdy/core/hpack/hpack_constants.h
index 3f4026b..d3f9d8d 100644
--- a/spdy/core/hpack/hpack_constants.h
+++ b/spdy/core/hpack/hpack_constants.h
@@ -12,7 +12,8 @@
 #include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h"
 
 // All section references below are to
-// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-08
+// https://httpwg.org/specs/rfc7540.html and
+// https://httpwg.org/specs/rfc7541.html.
 
 namespace spdy {
 
@@ -42,7 +43,7 @@
 class HpackHuffmanTable;
 class HpackStaticTable;
 
-// Defined in RFC 7540, 6.5.2.
+// RFC 7540, 6.5.2: Initial value for SETTINGS_HEADER_TABLE_SIZE.
 const uint32_t kDefaultHeaderTableSizeSetting = 4096;
 
 // RFC 7541, 5.2: Flag for a string literal that is stored unmodified (i.e.,
@@ -69,11 +70,11 @@
 // varint-encoded table size with a 5-bit prefix.
 const HpackPrefix kHeaderTableSizeUpdateOpcode = {0b001, 3};
 
-// Symbol code table from RFC 7541, "Appendix C. Huffman Code".
+// RFC 7541, Appendix B: Huffman Code.
 SPDY_EXPORT_PRIVATE const std::vector<HpackHuffmanSymbol>&
 HpackHuffmanCodeVector();
 
-// Static table from RFC 7541, "Appendix B. Static Table Definition".
+// RFC 7541, Appendix A: Static Table Definition.
 SPDY_EXPORT_PRIVATE const std::vector<HpackStaticEntry>&
 HpackStaticTableVector();
 
@@ -87,7 +88,7 @@
 // threads. This function is thread-safe.
 SPDY_EXPORT_PRIVATE const HpackStaticTable& ObtainHpackStaticTable();
 
-// Pseudo-headers start with a colon.  (HTTP2 8.1.2.1., HPACK 3.1.)
+// RFC 7541, 8.1.2.1: Pseudo-headers start with a colon.
 const char kPseudoHeaderPrefix = ':';
 
 }  // namespace spdy
diff --git a/spdy/core/hpack/hpack_decoder_adapter_test.cc b/spdy/core/hpack/hpack_decoder_adapter_test.cc
index 765c2ea..9c94126 100644
--- a/spdy/core/hpack/hpack_decoder_adapter_test.cc
+++ b/spdy/core/hpack/hpack_decoder_adapter_test.cc
@@ -259,15 +259,13 @@
   size_t bytes_passed_in_;
 };
 
-INSTANTIATE_TEST_CASE_P(
-    NoHandler,
-    HpackDecoderAdapterTest,
+INSTANTIATE_TEST_SUITE_P(
+    NoHandler, HpackDecoderAdapterTest,
     ::testing::Combine(::testing::Values(START_WITHOUT_HANDLER, NO_START),
                        ::testing::Bool()));
 
-INSTANTIATE_TEST_CASE_P(
-    WithHandler,
-    HpackDecoderAdapterTest,
+INSTANTIATE_TEST_SUITE_P(
+    WithHandler, HpackDecoderAdapterTest,
     ::testing::Combine(::testing::Values(START_WITH_HANDLER),
                        ::testing::Bool()));
 
diff --git a/spdy/core/hpack/hpack_encoder_test.cc b/spdy/core/hpack/hpack_encoder_test.cc
index 8ead1c2..7d7d37d 100644
--- a/spdy/core/hpack/hpack_encoder_test.cc
+++ b/spdy/core/hpack/hpack_encoder_test.cc
@@ -207,7 +207,8 @@
   bool use_incremental_;
 };
 
-INSTANTIATE_TEST_CASE_P(HpackEncoderTests, HpackEncoderTest, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(HpackEncoderTests, HpackEncoderTest,
+                         ::testing::Bool());
 
 TEST_P(HpackEncoderTest, SingleDynamicIndex) {
   encoder_.SetHeaderListener(
diff --git a/spdy/core/hpack/hpack_round_trip_test.cc b/spdy/core/hpack/hpack_round_trip_test.cc
index 4b3a848..bd4d6ee 100644
--- a/spdy/core/hpack/hpack_round_trip_test.cc
+++ b/spdy/core/hpack/hpack_round_trip_test.cc
@@ -78,11 +78,9 @@
   HpackDecoderAdapter decoder_;
 };
 
-INSTANTIATE_TEST_CASE_P(Tests,
-                        HpackRoundTripTest,
-                        ::testing::Values(ALL_INPUT,
-                                          ONE_BYTE,
-                                          ZERO_THEN_ONE_BYTE));
+INSTANTIATE_TEST_SUITE_P(Tests, HpackRoundTripTest,
+                         ::testing::Values(ALL_INPUT, ONE_BYTE,
+                                           ZERO_THEN_ONE_BYTE));
 
 TEST_P(HpackRoundTripTest, ResponseFixtures) {
   {
diff --git a/spdy/core/spdy_framer_test.cc b/spdy/core/spdy_framer_test.cc
index a08d7d7..3841172 100644
--- a/spdy/core/spdy_framer_test.cc
+++ b/spdy/core/spdy_framer_test.cc
@@ -615,9 +615,8 @@
   Http2DecoderAdapter deframer_;
 };
 
-INSTANTIATE_TEST_CASE_P(SpdyFramerTests,
-                        SpdyFramerTest,
-                        ::testing::Values(USE, NOT_USE));
+INSTANTIATE_TEST_SUITE_P(SpdyFramerTests, SpdyFramerTest,
+                         ::testing::Values(USE, NOT_USE));
 
 // Test that we can encode and decode a SpdyHeaderBlock in serialized form.
 TEST_P(SpdyFramerTest, HeaderBlockInBuffer) {