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.