Interop 105 first round of changes c2
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index d2b133d..23e4422 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -611,6 +611,7 @@
 }
 
 TEST_P(EndToEndTest, SimpleRequestResponseForcedVersionNegotiation) {
+  SetQuicReloadableFlag(quic_use_parse_public_header, true);
   client_supported_versions_.insert(client_supported_versions_.begin(),
                                     QuicVersionReservedForNegotiation());
   ASSERT_TRUE(Initialize());
@@ -623,6 +624,7 @@
 }
 
 TEST_P(EndToEndTestWithTls, ForcedVersionNegotiation) {
+  SetQuicReloadableFlag(quic_use_parse_public_header, true);
   client_supported_versions_.insert(client_supported_versions_.begin(),
                                     QuicVersionReservedForNegotiation());
   ASSERT_TRUE(Initialize());
@@ -2583,6 +2585,7 @@
       QuicFramer::BuildVersionNegotiationPacket(
           incorrect_connection_id, EmptyQuicConnectionId(),
           VersionHasIetfInvariantHeader(client_connection->transport_version()),
+          client_connection->version().HasLengthPrefixedConnectionIds(),
           server_supported_versions_));
   testing::NiceMock<MockQuicConnectionDebugVisitor> visitor;
   client_connection->set_debug_visitor(&visitor);
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index 2042f88..b4cfedd 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -1794,8 +1794,7 @@
   session_.OnStreamFrame(data1);
   EXPECT_EQ(1u, session_.GetNumOpenIncomingStreams());
   QuicStream* stream = session_.GetOrCreateStream(stream_id1);
-  EXPECT_EQ(1u, stream->flow_controller()->bytes_consumed());
-  EXPECT_EQ(1u, session_.flow_controller()->bytes_consumed());
+  EXPECT_EQ(7u, stream->flow_controller()->bytes_consumed());
 
   char unoptimized_type[] = {0x80, 0x00, 0x00, 0x01};
   data = std::string(unoptimized_type, 4) + "header";
@@ -1805,8 +1804,7 @@
   session_.OnStreamFrame(data2);
   EXPECT_EQ(2u, session_.GetNumOpenIncomingStreams());
   stream = session_.GetOrCreateStream(stream_id2);
-  EXPECT_EQ(4u, stream->flow_controller()->bytes_consumed());
-  EXPECT_EQ(5u, session_.flow_controller()->bytes_consumed());
+  EXPECT_EQ(10u, stream->flow_controller()->bytes_consumed());
 }
 
 TEST_P(QuicSpdySessionTestClient, Http3ServerPushOutofOrderFrame) {
diff --git a/quic/core/http/quic_spdy_stream.cc b/quic/core/http/quic_spdy_stream.cc
index 11cc233..d4c0211 100644
--- a/quic/core/http/quic_spdy_stream.cc
+++ b/quic/core/http/quic_spdy_stream.cc
@@ -47,7 +47,7 @@
         ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
   }
 
-  bool OnPriorityFrameStart(Http3FrameLengths /*frame_lengths*/) override {
+  bool OnPriorityFrameStart(Http3FrameLengths /*frame_length*/) override {
     CloseConnectionOnWrongFrame("Priority");
     return false;
   }
@@ -72,7 +72,7 @@
     return false;
   }
 
-  bool OnSettingsFrameStart(Http3FrameLengths /*frame_lengths*/) override {
+  bool OnSettingsFrameStart(Http3FrameLengths /*frame_length*/) override {
     CloseConnectionOnWrongFrame("Settings");
     return false;
   }
@@ -88,8 +88,8 @@
     return false;
   }
 
-  bool OnDataFrameStart(Http3FrameLengths frame_lengths) override {
-    return stream_->OnDataFrameStart(frame_lengths);
+  bool OnDataFrameStart(Http3FrameLengths frame_length) override {
+    return stream_->OnDataFrameStart(frame_length);
   }
 
   bool OnDataFramePayload(QuicStringPiece payload) override {
@@ -142,18 +142,16 @@
     return false;
   }
 
-  bool OnUnknownFrameStart(uint64_t /* frame_type */,
-                           Http3FrameLengths /* frame_length */) override {
-    // TODO(b/137554973): Consume frame header.
-    return true;
+  bool OnUnknownFrameStart(uint64_t frame_type,
+                           Http3FrameLengths frame_length) override {
+    return stream_->OnUnknownFrameStart(frame_type, frame_length);
   }
 
-  bool OnUnknownFramePayload(QuicStringPiece /* payload */) override {
-    // TODO(b/137554973): Consume frame payload.
-    return true;
+  bool OnUnknownFramePayload(QuicStringPiece payload) override {
+    return stream_->OnUnknownFramePayload(payload);
   }
 
-  bool OnUnknownFrameEnd() override { return true; }
+  bool OnUnknownFrameEnd() override { return stream_->OnUnknownFrameEnd(); }
 
  private:
   void CloseConnectionOnWrongFrame(std::string frame_type) {
@@ -184,7 +182,6 @@
       trailers_decompressed_(false),
       trailers_consumed_(false),
       priority_sent_(false),
-      headers_bytes_to_be_marked_consumed_(0),
       http_decoder_visitor_(QuicMakeUnique<HttpDecoderVisitor>(this)),
       decoder_(http_decoder_visitor_.get()),
       body_buffer_(sequencer()),
@@ -221,7 +218,6 @@
       trailers_decompressed_(false),
       trailers_consumed_(false),
       priority_sent_(false),
-      headers_bytes_to_be_marked_consumed_(0),
       http_decoder_visitor_(QuicMakeUnique<HttpDecoderVisitor>(this)),
       decoder_(http_decoder_visitor_.get()),
       body_buffer_(sequencer()),
@@ -398,12 +394,6 @@
   }
   size_t bytes_read = body_buffer_.ReadBody(iov, iov_len);
 
-  if (VersionUsesQpack(transport_version())) {
-    // Maybe all DATA frame bytes have been read and some trailing HEADERS had
-    // already been processed, in which case MarkConsumed() should be called.
-    MaybeMarkHeadersBytesConsumed();
-  }
-
   return bytes_read;
 }
 
@@ -422,12 +412,6 @@
     return;
   }
   body_buffer_.MarkBodyConsumed(num_bytes);
-
-  if (VersionUsesQpack(transport_version())) {
-    // Maybe all DATA frame bytes have been read and some trailing HEADERS had
-    // already been processed, in which case MarkConsumed() should be called.
-    MaybeMarkHeadersBytesConsumed();
-  }
 }
 
 bool QuicSpdyStream::IsDoneReading() const {
@@ -759,7 +743,7 @@
   spdy_session_ = nullptr;
 }
 
-bool QuicSpdyStream::OnDataFrameStart(Http3FrameLengths frame_lengths) {
+bool QuicSpdyStream::OnDataFrameStart(Http3FrameLengths frame_length) {
   DCHECK(VersionHasDataFrameHeader(transport_version()));
   if (!headers_decompressed_ || trailers_decompressed_) {
     // TODO(b/124216424): Change error code to HTTP_UNEXPECTED_FRAME.
@@ -769,7 +753,8 @@
     return false;
   }
 
-  body_buffer_.OnDataHeader(frame_lengths);
+  body_buffer_.OnConsumableBytes(frame_length.header_length);
+
   return true;
 }
 
@@ -777,6 +762,7 @@
   DCHECK(VersionHasDataFrameHeader(transport_version()));
 
   body_buffer_.OnDataPayload(payload);
+
   return true;
 }
 
@@ -822,16 +808,6 @@
   }
 }
 
-void QuicSpdyStream::MaybeMarkHeadersBytesConsumed() {
-  DCHECK(VersionUsesQpack(transport_version()));
-
-  if (!body_buffer_.HasBytesToRead() && !reading_stopped() &&
-      headers_bytes_to_be_marked_consumed_ > 0) {
-    sequencer()->MarkConsumed(headers_bytes_to_be_marked_consumed_);
-    headers_bytes_to_be_marked_consumed_ = 0;
-  }
-}
-
 QuicByteCount QuicSpdyStream::GetNumFrameHeadersInInterval(
     QuicStreamOffset offset,
     QuicByteCount data_length) const {
@@ -857,6 +833,8 @@
     return false;
   }
 
+  body_buffer_.OnConsumableBytes(frame_length.header_length);
+
   if (headers_decompressed_) {
     trailers_length_ = frame_length;
   } else {
@@ -868,10 +846,6 @@
           id(), spdy_session_->qpack_decoder(), this,
           spdy_session_->max_inbound_header_list_size());
 
-  // Do not call MaybeMarkHeadersBytesConsumed() yet, because
-  // HEADERS frame header bytes might not have been parsed completely.
-  headers_bytes_to_be_marked_consumed_ += frame_length.header_length;
-
   return true;
 }
 
@@ -880,8 +854,7 @@
 
   const bool success = qpack_decoded_headers_accumulator_->Decode(payload);
 
-  headers_bytes_to_be_marked_consumed_ += payload.size();
-  MaybeMarkHeadersBytesConsumed();
+  body_buffer_.OnConsumableBytes(payload.size());
 
   if (!success) {
     // TODO(124216424): Use HTTP_QPACK_DECOMPRESSION_FAILED error code.
@@ -919,6 +892,23 @@
   return !sequencer()->IsClosed() && !reading_stopped();
 }
 
+bool QuicSpdyStream::OnUnknownFrameStart(uint64_t /* frame_type */,
+                                         Http3FrameLengths frame_length) {
+  // Ignore unknown frames, but consume frame header.
+  body_buffer_.OnConsumableBytes(frame_length.header_length);
+  return true;
+}
+
+bool QuicSpdyStream::OnUnknownFramePayload(QuicStringPiece payload) {
+  // Ignore unknown frames, but consume frame payload.
+  body_buffer_.OnConsumableBytes(payload.size());
+  return true;
+}
+
+bool QuicSpdyStream::OnUnknownFrameEnd() {
+  return true;
+}
+
 void QuicSpdyStream::ProcessDecodedHeaders(const QuicHeaderList& headers) {
   const QuicByteCount frame_length = headers_decompressed_
                                          ? trailers_length_.payload_length
diff --git a/quic/core/http/quic_spdy_stream.h b/quic/core/http/quic_spdy_stream.h
index 9cd7827..d2fe9a1 100644
--- a/quic/core/http/quic_spdy_stream.h
+++ b/quic/core/http/quic_spdy_stream.h
@@ -254,14 +254,13 @@
   bool OnHeadersFrameStart(Http3FrameLengths frame_length);
   bool OnHeadersFramePayload(QuicStringPiece payload);
   bool OnHeadersFrameEnd();
+  bool OnUnknownFrameStart(uint64_t frame_type, Http3FrameLengths frame_length);
+  bool OnUnknownFramePayload(QuicStringPiece payload);
+  bool OnUnknownFrameEnd();
 
   // Called internally when headers are decoded.
   void ProcessDecodedHeaders(const QuicHeaderList& headers);
 
-  // Call QuicStreamSequencer::MarkConsumed() with
-  // |headers_bytes_to_be_marked_consumed_| if appropriate.
-  void MaybeMarkHeadersBytesConsumed();
-
   // Given the interval marked by [|offset|, |offset| + |data_length|), return
   // the number of frame header bytes contained in it.
   QuicByteCount GetNumFrameHeadersInInterval(QuicStreamOffset offset,
@@ -294,9 +293,6 @@
   // True if the stream has already sent an priority frame.
   bool priority_sent_;
 
-  // Number of bytes consumed while decoding HEADERS frames that cannot be
-  // marked consumed in QuicStreamSequencer until later.
-  QuicByteCount headers_bytes_to_be_marked_consumed_;
   // The parsed trailers received from the peer.
   spdy::SpdyHeaderBlock received_trailers_;
 
diff --git a/quic/core/http/quic_spdy_stream_body_buffer.cc b/quic/core/http/quic_spdy_stream_body_buffer.cc
index c0a77b2..61d37c9 100644
--- a/quic/core/http/quic_spdy_stream_body_buffer.cc
+++ b/quic/core/http/quic_spdy_stream_body_buffer.cc
@@ -3,31 +3,117 @@
 // found in the LICENSE file.
 
 #include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.h"
+
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
 
 namespace quic {
 
+QuicSpdyStreamBodyBuffer::QuicSpdyStreamConsumeManager::
+    QuicSpdyStreamConsumeManager(ConsumeFunction consume_function)
+    : consume_function_(std::move(consume_function)) {}
+
+void QuicSpdyStreamBodyBuffer::QuicSpdyStreamConsumeManager::OnConsumableBytes(
+    QuicByteCount length) {
+  DCHECK_NE(0u, length);
+
+  if (fragments_.empty()) {
+    consume_function_(length);
+    return;
+  }
+
+  DCHECK(!fragments_.front().consumable);
+  fragments_.push_back({length, /* consumable = */ true});
+}
+
+void QuicSpdyStreamBodyBuffer::QuicSpdyStreamConsumeManager::OnDataPayload(
+    QuicByteCount length) {
+  DCHECK_NE(0u, length);
+
+  fragments_.push_back({length, /* consumable = */ false});
+}
+
+void QuicSpdyStreamBodyBuffer::QuicSpdyStreamConsumeManager::ConsumeData(
+    QuicByteCount length) {
+  if (length == 0) {
+    return;
+  }
+
+  DCHECK(!fragments_.empty());
+  DCHECK(!fragments_.front().consumable);
+
+  QuicByteCount remaining_length = length;
+  QuicByteCount bytes_to_consume = 0;
+
+  do {
+    const Fragment& fragment = fragments_.front();
+
+    if (fragment.consumable) {
+      bytes_to_consume += fragment.length;
+      fragments_.pop_front();
+      continue;
+    }
+
+    if (remaining_length == 0) {
+      break;
+    }
+
+    if (fragment.length <= remaining_length) {
+      bytes_to_consume += fragment.length;
+      remaining_length -= fragment.length;
+      fragments_.pop_front();
+      // Continue iterating even if |remaining_length| to make sure consumable
+      // bytes on the front of the queue are consumed.
+      continue;
+    }
+
+    bytes_to_consume += remaining_length;
+    QuicByteCount new_fragement_length = fragment.length - remaining_length;
+    remaining_length = 0;
+    fragments_.pop_front();
+    fragments_.push_front({new_fragement_length, /* consumable = */ false});
+    break;
+  } while (!fragments_.empty());
+
+  DCHECK_EQ(0u, remaining_length);
+  if (!fragments_.empty()) {
+    DCHECK(!fragments_.front().consumable);
+  }
+
+  consume_function_(bytes_to_consume);
+}
+
 QuicSpdyStreamBodyBuffer::QuicSpdyStreamBodyBuffer(
     QuicStreamSequencer* sequencer)
+    : QuicSpdyStreamBodyBuffer(std::bind(&QuicStreamSequencer::MarkConsumed,
+                                         sequencer,
+                                         std::placeholders::_1)) {}
+
+QuicSpdyStreamBodyBuffer::QuicSpdyStreamBodyBuffer(
+    ConsumeFunction consume_function)
     : bytes_remaining_(0),
       total_body_bytes_readable_(0),
       total_body_bytes_received_(0),
-      total_payload_lengths_(0),
-      sequencer_(sequencer) {}
+      consume_manager_(std::move(consume_function)) {}
 
 QuicSpdyStreamBodyBuffer::~QuicSpdyStreamBodyBuffer() {}
 
-void QuicSpdyStreamBodyBuffer::OnDataHeader(Http3FrameLengths frame_lengths) {
-  frame_meta_.push_back(frame_lengths);
-  total_payload_lengths_ += frame_lengths.payload_length;
+void QuicSpdyStreamBodyBuffer::OnConsumableBytes(QuicByteCount length) {
+  DCHECK_NE(0u, length);
+
+  consume_manager_.OnConsumableBytes(length);
 }
 
 void QuicSpdyStreamBodyBuffer::OnDataPayload(QuicStringPiece payload) {
   DCHECK(!payload.empty());
+
+  consume_manager_.OnDataPayload(payload.length());
+
   bodies_.push_back(payload);
   total_body_bytes_received_ += payload.length();
   total_body_bytes_readable_ += payload.length();
-  DCHECK_LE(total_body_bytes_received_, total_payload_lengths_);
 }
 
 void QuicSpdyStreamBodyBuffer::MarkBodyConsumed(size_t num_bytes) {
@@ -56,18 +142,11 @@
       remaining = 0;
     }
   }
-  // Consume headers.
-  while (bytes_remaining_ < num_bytes) {
-    if (frame_meta_.empty()) {
-      QUIC_BUG << "Faild to consume because frame header buffer is empty.";
-      return;
-    }
-    auto meta = frame_meta_.front();
-    frame_meta_.pop_front();
-    bytes_remaining_ += meta.payload_length;
-    sequencer_->MarkConsumed(meta.header_length);
-  }
-  sequencer_->MarkConsumed(num_bytes);
+
+  // Consume DATA frame payloads and optionally other data (like DATA frame
+  // headers).
+  consume_manager_.ConsumeData(num_bytes);
+
   // Update accountings.
   bytes_remaining_ -= num_bytes;
   total_body_bytes_readable_ -= num_bytes;
diff --git a/quic/core/http/quic_spdy_stream_body_buffer.h b/quic/core/http/quic_spdy_stream_body_buffer.h
index 2485aac..36418e9 100644
--- a/quic/core/http/quic_spdy_stream_body_buffer.h
+++ b/quic/core/http/quic_spdy_stream_body_buffer.h
@@ -5,34 +5,90 @@
 #ifndef QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_STREAM_BODY_BUFFER_H_
 #define QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_STREAM_BODY_BUFFER_H_
 
+#include <functional>
+#include <list>
+
 #include "net/third_party/quiche/src/quic/core/http/http_decoder.h"
-#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.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_iovec.h"
 
 namespace quic {
 
-// Buffer decoded body for QuicSpdyStream. It also talks to the sequencer to
-// consume data.
+class QuicStreamSequencer;
+
+// Keep references to decoded body (DATA frame payload) fragments, and manage
+// calling QuicStreamSequencer::MarkConsumed() for all data received on the
+// stream.
 class QUIC_EXPORT_PRIVATE QuicSpdyStreamBodyBuffer {
+ private:
+  using ConsumeFunction = std::function<void(size_t)>;
+
+  // Class that calls QuicStreamSequencer::MarkConsumed() appropriately as DATA
+  // frame payload is consumed by higher layers.
+  class QUIC_EXPORT_PRIVATE QuicSpdyStreamConsumeManager {
+   public:
+    explicit QuicSpdyStreamConsumeManager(ConsumeFunction consume_function);
+    ~QuicSpdyStreamConsumeManager() = default;
+
+    // Called when data that could immediately be marked consumed with the
+    // sequencer (provided that all previous DATA frame payloads are consumed)
+    // is received.
+    void OnConsumableBytes(QuicByteCount length);
+
+    // Called when DATA frame payload is received.  This cannot be marked
+    // consumed with the sequencer until higher layers consume it and
+    // ConsumeData() is called.  |length| must be positive.
+    void OnDataPayload(QuicByteCount length);
+
+    // Called when some amount of DATA frame payload is consumed by higher
+    // layers. |length| bytes of DATA payload as well as all interleaving and
+    // immediately following consumable data are marked consumed with the
+    // sequencer.  Must not be called with larger |length| than total currently
+    // unconsumed DATA payload.
+    void ConsumeData(QuicByteCount length);
+
+   private:
+    struct Fragment {
+      QuicByteCount length;
+      bool consumable;
+    };
+
+    // Queue of data fragments.
+    // The front of the queue must not be consumable (otherwise it should be
+    // immediately consumed).  Fragments must not be empty.
+    std::list<Fragment> fragments_;
+
+    ConsumeFunction consume_function_;
+  };
+
  public:
   // QuicSpdyStreamBodyBuffer doesn't own the sequencer and the sequencer can
   // outlive the buffer.
   explicit QuicSpdyStreamBodyBuffer(QuicStreamSequencer* sequencer);
 
+  // Used for tests.
+  explicit QuicSpdyStreamBodyBuffer(ConsumeFunction consume_function);
+
   ~QuicSpdyStreamBodyBuffer();
 
-  // Add metadata of the frame to accountings.
-  // Called when QuicSpdyStream receives data frame header.
-  void OnDataHeader(Http3FrameLengths frame_lengths);
+  // One of the following two methods must be called every time data is received
+  // on the request stream.
 
-  // Add new data payload to buffer.
-  // Called when QuicSpdyStream received data payload.
-  // Data pointed by payload must be alive until consumed by
-  // QuicStreamSequencer::MarkConsumed().
+  // Called when data that could immediately be marked consumed with the
+  // sequencer (provided that all previous DATA frame payloads are consumed) is
+  // received.  |length| must be positive.
+  void OnConsumableBytes(QuicByteCount length);
+
+  // Called when DATA frame payload is received.  |payload| is added to the
+  // buffer.  The data pointed to by |payload| is kept alive until a
+  // MarkBodyConsumed() or ReadBody() call consumes it.  Data must be owned by
+  // QuicStreamSequencer.  |payload| must not be empty.
   void OnDataPayload(QuicStringPiece payload);
 
-  // Take |num_bytes| as the body size, calculate header sizes accordingly, and
-  // consume the right amount of data in the stream sequencer.
+  // Consume |num_bytes| of DATA frame payload, and an other interleaved or
+  // immediately succeeding consumable bytes.
   void MarkBodyConsumed(size_t num_bytes);
 
   // Fill up to |iov_len| with bodies available in buffer. No data is consumed.
@@ -41,9 +97,9 @@
   // Returns the number of iov used.
   int PeekBody(iovec* iov, size_t iov_len) const;
 
-  // Copies from buffer into |iov| up to |iov_len|, and consume data in
-  // sequencer. |iov.iov_base| and |iov.iov_len| are preassigned and will not be
-  // changed.
+  // Copies from buffer into |iov| up to |iov_len|, and calls
+  // QuicSpdyStreamConsumeManager::ConsumeData() with number of bytes read.
+  // |iov.iov_base| and |iov.iov_len| are preassigned and will not be changed.
   // Returns the number of bytes read.
   size_t ReadBody(const struct iovec* iov, size_t iov_len);
 
@@ -56,18 +112,14 @@
  private:
   // Storage for decoded data.
   QuicDeque<QuicStringPiece> bodies_;
-  // Storage for header lengths.
-  QuicDeque<Http3FrameLengths> frame_meta_;
   // Bytes in the first available data frame that are not consumed yet.
   QuicByteCount bytes_remaining_;
   // Total available body data in the stream.
   QuicByteCount total_body_bytes_readable_;
   // Total bytes read from the stream excluding headers.
   QuicByteCount total_body_bytes_received_;
-  // Total length of payloads tracked by frame_meta_.
-  QuicByteCount total_payload_lengths_;
-  // Stream sequencer that directly manages data in the stream.
-  QuicStreamSequencer* sequencer_;
+  // Consume manager that talks to the stream sequencer.
+  QuicSpdyStreamConsumeManager consume_manager_;
 };
 
 }  // namespace quic
diff --git a/quic/core/http/quic_spdy_stream_body_buffer_test.cc b/quic/core/http/quic_spdy_stream_body_buffer_test.cc
index 6e8e20d..37265f4 100644
--- a/quic/core/http/quic_spdy_stream_body_buffer_test.cc
+++ b/quic/core/http/quic_spdy_stream_body_buffer_test.cc
@@ -20,60 +20,47 @@
 
 namespace {
 
-class MockStream : public QuicStreamSequencer::StreamInterface {
+class MockConsumer {
  public:
-  MOCK_METHOD0(OnFinRead, void());
-  MOCK_METHOD0(OnDataAvailable, void());
-  MOCK_METHOD2(CloseConnectionWithDetails,
-               void(QuicErrorCode error, const std::string& details));
-  MOCK_METHOD1(Reset, void(QuicRstStreamErrorCode error));
-  MOCK_METHOD0(OnCanWrite, void());
-  MOCK_METHOD1(AddBytesConsumed, void(QuicByteCount bytes));
-
-  QuicStreamId id() const override { return 1; }
-
-  const QuicSocketAddress& PeerAddressOfLatestPacket() const override {
-    return peer_address_;
-  }
-
- protected:
-  QuicSocketAddress peer_address_ =
-      QuicSocketAddress(QuicIpAddress::Any4(), 65535);
-};
-
-class MockSequencer : public QuicStreamSequencer {
- public:
-  explicit MockSequencer(MockStream* stream) : QuicStreamSequencer(stream) {}
-  virtual ~MockSequencer() = default;
   MOCK_METHOD1(MarkConsumed, void(size_t num_bytes_consumed));
 };
 
 class QuicSpdyStreamBodyBufferTest : public QuicTest {
  public:
   QuicSpdyStreamBodyBufferTest()
-      : sequencer_(&stream_), body_buffer_(&sequencer_) {}
+      : body_buffer_(std::bind(&MockConsumer::MarkConsumed,
+                               &consumer_,
+                               std::placeholders::_1)) {}
 
  protected:
-  MockStream stream_;
-  MockSequencer sequencer_;
+  testing::StrictMock<MockConsumer> consumer_;
   QuicSpdyStreamBodyBuffer body_buffer_;
   HttpEncoder encoder_;
 };
 
-TEST_F(QuicSpdyStreamBodyBufferTest, ReceiveBodies) {
+TEST_F(QuicSpdyStreamBodyBufferTest, HasBytesToRead) {
+  const QuicByteCount header_length = 3;
   std::string body(1024, 'a');
+
+  EXPECT_CALL(consumer_, MarkConsumed(header_length));
+  body_buffer_.OnConsumableBytes(header_length);
+
   EXPECT_FALSE(body_buffer_.HasBytesToRead());
-  body_buffer_.OnDataHeader(Http3FrameLengths(3, 1024));
-  body_buffer_.OnDataPayload(QuicStringPiece(body));
-  EXPECT_EQ(1024u, body_buffer_.total_body_bytes_received());
+  EXPECT_EQ(0u, body_buffer_.total_body_bytes_received());
+
+  body_buffer_.OnDataPayload(body);
   EXPECT_TRUE(body_buffer_.HasBytesToRead());
+  EXPECT_EQ(1024u, body_buffer_.total_body_bytes_received());
 }
 
 TEST_F(QuicSpdyStreamBodyBufferTest, PeekBody) {
+  const QuicByteCount header_length = 3;
   std::string body(1024, 'a');
-  body_buffer_.OnDataHeader(Http3FrameLengths(3, 1024));
-  body_buffer_.OnDataPayload(QuicStringPiece(body));
-  EXPECT_EQ(1024u, body_buffer_.total_body_bytes_received());
+
+  EXPECT_CALL(consumer_, MarkConsumed(header_length));
+  body_buffer_.OnConsumableBytes(header_length);
+  body_buffer_.OnDataPayload(body);
+
   iovec vec;
   EXPECT_EQ(1, body_buffer_.PeekBody(&vec, 1));
   EXPECT_EQ(1024u, vec.iov_len);
@@ -81,67 +68,59 @@
             QuicStringPiece(static_cast<const char*>(vec.iov_base), 1024));
 }
 
-// Buffer only receives 1 frame. Stream consumes less or equal than a frame.
+// Buffer receives one frame. Stream consumes payload in fragments.
 TEST_F(QuicSpdyStreamBodyBufferTest, MarkConsumedPartialSingleFrame) {
   testing::InSequence seq;
+
+  const QuicByteCount header_length = 3;
   std::string body(1024, 'a');
-  std::unique_ptr<char[]> buffer;
-  QuicByteCount header_length =
-      encoder_.SerializeDataFrameHeader(body.length(), &buffer);
-  std::string header = std::string(buffer.get(), header_length);
-  Http3FrameLengths lengths(header_length, 1024);
-  std::string data = header + body;
-  QuicStreamFrame frame(1, false, 0, data);
-  sequencer_.OnStreamFrame(frame);
-  body_buffer_.OnDataHeader(lengths);
-  body_buffer_.OnDataPayload(QuicStringPiece(body));
-  EXPECT_CALL(stream_, AddBytesConsumed(header_length));
-  EXPECT_CALL(stream_, AddBytesConsumed(1024));
-  body_buffer_.MarkBodyConsumed(1024);
+
+  EXPECT_CALL(consumer_, MarkConsumed(header_length));
+  body_buffer_.OnConsumableBytes(header_length);
+  body_buffer_.OnDataPayload(body);
+
+  EXPECT_CALL(consumer_, MarkConsumed(512));
+  body_buffer_.MarkBodyConsumed(512);
+
+  EXPECT_CALL(consumer_, MarkConsumed(512));
+  body_buffer_.MarkBodyConsumed(512);
 }
 
-// Buffer received 2 frames. Stream consumes multiple times.
+// Buffer receives two frames. Stream consumes multiple times.
 TEST_F(QuicSpdyStreamBodyBufferTest, MarkConsumedMultipleFrames) {
   testing::InSequence seq;
-  // 1st frame.
+
+  const QuicByteCount header_length1 = 3;
   std::string body1(1024, 'a');
-  std::unique_ptr<char[]> buffer;
-  QuicByteCount header_length1 =
-      encoder_.SerializeDataFrameHeader(body1.length(), &buffer);
-  std::string header1 = std::string(buffer.get(), header_length1);
-  Http3FrameLengths lengths1(header_length1, 1024);
-  std::string data1 = header1 + body1;
-  QuicStreamFrame frame1(1, false, 0, data1);
-  sequencer_.OnStreamFrame(frame1);
-  body_buffer_.OnDataHeader(lengths1);
-  body_buffer_.OnDataPayload(QuicStringPiece(body1));
 
-  // 2nd frame.
+  EXPECT_CALL(consumer_, MarkConsumed(header_length1));
+  body_buffer_.OnConsumableBytes(header_length1);
+  body_buffer_.OnDataPayload(body1);
+
+  const QuicByteCount header_length2 = 3;
   std::string body2(2048, 'b');
-  QuicByteCount header_length2 =
-      encoder_.SerializeDataFrameHeader(body2.length(), &buffer);
-  std::string header2 = std::string(buffer.get(), header_length2);
-  Http3FrameLengths lengths2(header_length2, 2048);
-  std::string data2 = header2 + body2;
-  QuicStreamFrame frame2(1, false, data1.length(), data2);
-  sequencer_.OnStreamFrame(frame2);
-  body_buffer_.OnDataHeader(lengths2);
-  body_buffer_.OnDataPayload(QuicStringPiece(body2));
+  body_buffer_.OnConsumableBytes(header_length2);
+  body_buffer_.OnDataPayload(body2);
 
-  EXPECT_CALL(stream_, AddBytesConsumed(header_length1));
-  EXPECT_CALL(stream_, AddBytesConsumed(512));
+  // Consume part of the first frame payload.
+  EXPECT_CALL(consumer_, MarkConsumed(512));
   body_buffer_.MarkBodyConsumed(512);
-  EXPECT_CALL(stream_, AddBytesConsumed(header_length2));
-  EXPECT_CALL(stream_, AddBytesConsumed(2048));
+
+  // Consume rest of the first frame and some of the second.
+  EXPECT_CALL(consumer_, MarkConsumed(header_length2 + 2048));
   body_buffer_.MarkBodyConsumed(2048);
-  EXPECT_CALL(stream_, AddBytesConsumed(512));
+
+  // Consume rest of the second frame.
+  EXPECT_CALL(consumer_, MarkConsumed(512));
   body_buffer_.MarkBodyConsumed(512);
 }
 
 TEST_F(QuicSpdyStreamBodyBufferTest, MarkConsumedMoreThanBuffered) {
+  const QuicByteCount header_length = 3;
+  EXPECT_CALL(consumer_, MarkConsumed(header_length));
+  body_buffer_.OnConsumableBytes(header_length);
+
   std::string body(1024, 'a');
-  Http3FrameLengths lengths(3, 1024);
-  body_buffer_.OnDataHeader(lengths);
   body_buffer_.OnDataPayload(body);
   EXPECT_QUIC_BUG(
       body_buffer_.MarkBodyConsumed(2048),
@@ -149,24 +128,18 @@
       "enough bytes available. Total bytes readable are: 1024");
 }
 
-// Buffer receives 1 frame. Stream read from the buffer.
+// Buffer receives one frame. Stream reads from the buffer.
 TEST_F(QuicSpdyStreamBodyBufferTest, ReadSingleBody) {
   testing::InSequence seq;
+
+  const QuicByteCount header_length = 3;
   std::string body(1024, 'a');
-  std::unique_ptr<char[]> buffer;
-  QuicByteCount header_length =
-      encoder_.SerializeDataFrameHeader(body.length(), &buffer);
-  std::string header = std::string(buffer.get(), header_length);
-  Http3FrameLengths lengths(header_length, 1024);
-  std::string data = header + body;
-  QuicStreamFrame frame(1, false, 0, data);
-  sequencer_.OnStreamFrame(frame);
-  body_buffer_.OnDataHeader(lengths);
+
+  EXPECT_CALL(consumer_, MarkConsumed(header_length));
+  body_buffer_.OnConsumableBytes(header_length);
   body_buffer_.OnDataPayload(QuicStringPiece(body));
 
-  EXPECT_CALL(stream_, AddBytesConsumed(header_length));
-  EXPECT_CALL(stream_, AddBytesConsumed(1024));
-
+  EXPECT_CALL(consumer_, MarkConsumed(1024));
   char base[1024];
   iovec iov = {&base[0], 1024};
   EXPECT_EQ(1024u, body_buffer_.ReadBody(&iov, 1));
@@ -175,37 +148,24 @@
             QuicStringPiece(static_cast<const char*>(iov.iov_base), 1024));
 }
 
-// Buffer receives 2 frames, stream read from the buffer multiple times.
+// Buffer receives two frames. Stream reads from the buffer multiple times.
 TEST_F(QuicSpdyStreamBodyBufferTest, ReadMultipleBody) {
   testing::InSequence seq;
-  // 1st frame.
+
+  const QuicByteCount header_length1 = 3;
   std::string body1(1024, 'a');
-  std::unique_ptr<char[]> buffer;
-  QuicByteCount header_length1 =
-      encoder_.SerializeDataFrameHeader(body1.length(), &buffer);
-  std::string header1 = std::string(buffer.get(), header_length1);
-  Http3FrameLengths lengths1(header_length1, 1024);
-  std::string data1 = header1 + body1;
-  QuicStreamFrame frame1(1, false, 0, data1);
-  sequencer_.OnStreamFrame(frame1);
-  body_buffer_.OnDataHeader(lengths1);
-  body_buffer_.OnDataPayload(QuicStringPiece(body1));
 
-  // 2nd frame.
+  EXPECT_CALL(consumer_, MarkConsumed(header_length1));
+  body_buffer_.OnConsumableBytes(header_length1);
+  body_buffer_.OnDataPayload(body1);
+
+  const QuicByteCount header_length2 = 3;
   std::string body2(2048, 'b');
-  QuicByteCount header_length2 =
-      encoder_.SerializeDataFrameHeader(body2.length(), &buffer);
-  std::string header2 = std::string(buffer.get(), header_length2);
-  Http3FrameLengths lengths2(header_length2, 2048);
-  std::string data2 = header2 + body2;
-  QuicStreamFrame frame2(1, false, data1.length(), data2);
-  sequencer_.OnStreamFrame(frame2);
-  body_buffer_.OnDataHeader(lengths2);
-  body_buffer_.OnDataPayload(QuicStringPiece(body2));
+  body_buffer_.OnConsumableBytes(header_length2);
+  body_buffer_.OnDataPayload(body2);
 
-  // First read of 512 bytes.
-  EXPECT_CALL(stream_, AddBytesConsumed(header_length1));
-  EXPECT_CALL(stream_, AddBytesConsumed(512));
+  // Read part of the first frame payload.
+  EXPECT_CALL(consumer_, MarkConsumed(512));
   char base[512];
   iovec iov = {&base[0], 512};
   EXPECT_EQ(512u, body_buffer_.ReadBody(&iov, 1));
@@ -213,9 +173,8 @@
   EXPECT_EQ(body1.substr(0, 512),
             QuicStringPiece(static_cast<const char*>(iov.iov_base), 512));
 
-  // Second read of 2048 bytes.
-  EXPECT_CALL(stream_, AddBytesConsumed(header_length2));
-  EXPECT_CALL(stream_, AddBytesConsumed(2048));
+  // Read rest of the first frame and some of the second.
+  EXPECT_CALL(consumer_, MarkConsumed(header_length2 + 2048));
   char base2[2048];
   iovec iov2 = {&base2[0], 2048};
   EXPECT_EQ(2048u, body_buffer_.ReadBody(&iov2, 1));
@@ -223,8 +182,8 @@
   EXPECT_EQ(body1.substr(512, 512) + body2.substr(0, 1536),
             QuicStringPiece(static_cast<const char*>(iov2.iov_base), 2048));
 
-  // Third read of the rest 512 bytes.
-  EXPECT_CALL(stream_, AddBytesConsumed(512));
+  // Read rest of the second frame.
+  EXPECT_CALL(consumer_, MarkConsumed(512));
   char base3[512];
   iovec iov3 = {&base3[0], 512};
   EXPECT_EQ(512u, body_buffer_.ReadBody(&iov3, 1));
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc
index f9777ec..03a1719 100644
--- a/quic/core/http/quic_spdy_stream_test.cc
+++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -2088,8 +2088,7 @@
       HeadersFrame(QuicTextUtils::HexDecode("00002a94e703626172"));
 
   // All HEADERS frame bytes are consumed even if the frame is not received
-  // completely (as long as at least some of the payload is received, which is
-  // an implementation detail that should not be tested).
+  // completely.
   OnStreamFrame(QuicStringPiece(headers).substr(0, headers.size() - 1));
   EXPECT_EQ(headers.size() - 1, NewlyConsumedBytes());
 
@@ -2103,17 +2102,22 @@
 
   // DATA frame.
   QuicStringPiece data_payload(kDataFramePayload);
-  std::string data_frame = DataFrame(data_payload);
+  std::unique_ptr<char[]> data_buffer;
+  QuicByteCount data_frame_header_length =
+      encoder_.SerializeDataFrameHeader(data_payload.size(), &data_buffer);
+  QuicStringPiece data_frame_header(data_buffer.get(),
+                                    data_frame_header_length);
+  std::string data_frame = QuicStrCat(data_frame_header, data_payload);
 
-  // DATA frame is not consumed because payload has to be buffered.
-  // TODO(bnc): Consume frame header as soon as possible.
+  // DATA frame header is consumed.
+  // DATA frame payload is not consumed because payload has to be buffered.
   OnStreamFrame(data_frame);
-  EXPECT_EQ(0u, NewlyConsumedBytes());
+  EXPECT_EQ(data_frame_header_length, NewlyConsumedBytes());
 
   // Consume all but last byte of data.
   EXPECT_EQ(data_payload.substr(0, data_payload.size() - 1),
             ReadFromStream(data_payload.size() - 1));
-  EXPECT_EQ(data_frame.size() - 1, NewlyConsumedBytes());
+  EXPECT_EQ(data_payload.size() - 1, NewlyConsumedBytes());
 
   // Trailing HEADERS frame with QPACK encoded
   // single header field "custom-key: custom-value".
@@ -2320,6 +2324,27 @@
   EXPECT_EQ(0u, stream_->sequencer()->NumBytesConsumed());
 }
 
+// Regression test for b/137554973: unknown frames should be consumed.
+TEST_P(QuicSpdyStreamTest, ConsumeUnknownFrame) {
+  if (!VersionUsesQpack(GetParam().transport_version)) {
+    return;
+  }
+
+  Initialize(kShouldProcessData);
+
+  std::string unknown_frame = QuicTextUtils::HexDecode(
+      "21"        // reserved frame type
+      "03"        // payload length
+      "666f6f");  // payload "foo"
+
+  EXPECT_EQ(0u, stream_->flow_controller()->bytes_consumed());
+
+  stream_->OnStreamFrame(QuicStreamFrame(stream_->id(), /* fin = */ false,
+                                         /* offset = */ 0, unknown_frame));
+
+  EXPECT_EQ(unknown_frame.size(), stream_->flow_controller()->bytes_consumed());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 22ee18a..b983a94 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -247,6 +247,7 @@
       max_tracked_packets_(GetQuicFlag(FLAGS_quic_max_tracked_packet_count)),
       pending_version_negotiation_packet_(false),
       send_ietf_version_negotiation_packet_(false),
+      send_version_negotiation_packet_with_prefixed_lengths_(false),
       idle_timeout_connection_close_behavior_(
           ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET),
       close_connection_after_five_rtos_(false),
@@ -1452,9 +1453,11 @@
   }
 }
 
-void QuicConnection::SendVersionNegotiationPacket(bool ietf_quic) {
+void QuicConnection::SendVersionNegotiationPacket(bool ietf_quic,
+                                                  bool has_length_prefix) {
   pending_version_negotiation_packet_ = true;
   send_ietf_version_negotiation_packet_ = ietf_quic;
+  send_version_negotiation_packet_with_prefixed_lengths_ = has_length_prefix;
 
   if (HandleWriteBlocked()) {
     return;
@@ -1466,7 +1469,7 @@
                   << "}, " << (ietf_quic ? "" : "!") << "ietf_quic";
   std::unique_ptr<QuicEncryptedPacket> version_packet(
       packet_generator_.SerializeVersionNegotiationPacket(
-          ietf_quic, framer_.supported_versions()));
+          ietf_quic, has_length_prefix, framer_.supported_versions()));
   QUIC_DVLOG(2) << ENDPOINT << "Sending version negotiation packet: {"
                 << ParsedQuicVersionVectorToString(framer_.supported_versions())
                 << "}, " << (ietf_quic ? "" : "!") << "ietf_quic:" << std::endl
@@ -1865,7 +1868,9 @@
   DCHECK(!writer_->IsWriteBlocked());
 
   if (pending_version_negotiation_packet_) {
-    SendVersionNegotiationPacket(send_ietf_version_negotiation_packet_);
+    SendVersionNegotiationPacket(
+        send_ietf_version_negotiation_packet_,
+        send_version_negotiation_packet_with_prefixed_lengths_);
   }
 
   QUIC_CLIENT_HISTOGRAM_COUNTS("QuicSession.NumQueuedPacketsBeforeWrite",
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 59a3656..45d31be 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -980,7 +980,7 @@
       const QuicStopWaitingFrame& stop_waiting);
 
   // Sends a version negotiation packet to the peer.
-  void SendVersionNegotiationPacket(bool ietf_quic);
+  void SendVersionNegotiationPacket(bool ietf_quic, bool has_length_prefix);
 
   // Clears any accumulated frames from the last received packet.
   void ClearLastFrames();
@@ -1217,6 +1217,7 @@
   bool pending_version_negotiation_packet_;
   // Used when pending_version_negotiation_packet_ is true.
   bool send_ietf_version_negotiation_packet_;
+  bool send_version_negotiation_packet_with_prefixed_lengths_;
 
   // When packets could not be sent because the socket was not writable,
   // they are added to this list.  All corresponding frames are in
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index ee7b651..f0152cd 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -6774,16 +6774,20 @@
 }
 
 TEST_P(QuicConnectionTest, ClientHandlesVersionNegotiation) {
-  // Start out with an unsupported version.
-  QuicConnectionPeer::GetFramer(&connection_)
-      ->set_version_for_tests(QuicVersionReservedForNegotiation());
+  // All supported versions except the one the connection supports.
+  ParsedQuicVersionVector versions;
+  for (auto version : AllSupportedVersions()) {
+    if (version != connection_.version()) {
+      versions.push_back(version);
+    }
+  }
 
   // Send a version negotiation packet.
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       QuicFramer::BuildVersionNegotiationPacket(
           connection_id_, EmptyQuicConnectionId(),
           VersionHasIetfInvariantHeader(connection_.transport_version()),
-          AllSupportedVersions()));
+          connection_.version().HasLengthPrefixedConnectionIds(), versions));
   std::unique_ptr<QuicReceivedPacket> received(
       ConstructReceivedPacket(*encrypted, QuicTime::Zero()));
   EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF))
@@ -6804,6 +6808,7 @@
       QuicFramer::BuildVersionNegotiationPacket(
           connection_id_, EmptyQuicConnectionId(),
           VersionHasIetfInvariantHeader(connection_.transport_version()),
+          connection_.version().HasLengthPrefixedConnectionIds(),
           AllSupportedVersions()));
   std::unique_ptr<QuicReceivedPacket> received(
       ConstructReceivedPacket(*encrypted, QuicTime::Zero()));
diff --git a/quic/core/quic_constants.h b/quic/core/quic_constants.h
index 442fb09..77b0b8a 100644
--- a/quic/core/quic_constants.h
+++ b/quic/core/quic_constants.h
@@ -13,6 +13,8 @@
 #include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 
+// useless comment.
+
 // Definitions of constant values used throughout the QUIC code.
 
 namespace quic {
diff --git a/quic/core/quic_crypto_stream_test.cc b/quic/core/quic_crypto_stream_test.cc
index dd4450e..4117786 100644
--- a/quic/core/quic_crypto_stream_test.cc
+++ b/quic/core/quic_crypto_stream_test.cc
@@ -519,10 +519,13 @@
     SCOPED_TRACE(version);
     QuicByteCount expected_overhead = 48;
     if (VersionHasIetfInvariantHeader(version)) {
-      expected_overhead = 52;
+      expected_overhead += 4;
     }
     if (QuicVersionHasLongHeaderLengths(version)) {
-      expected_overhead = 55;
+      expected_overhead += 3;
+    }
+    if (VersionHasLengthPrefixedConnectionIds(version)) {
+      expected_overhead += 1;
     }
     EXPECT_EQ(expected_overhead, QuicCryptoStream::CryptoMessageFramingOverhead(
                                      version, TestConnectionId()));
diff --git a/quic/core/quic_dispatcher.cc b/quic/core/quic_dispatcher.cc
index c6ee816..7522703 100644
--- a/quic/core/quic_dispatcher.cc
+++ b/quic/core/quic_dispatcher.cc
@@ -228,11 +228,25 @@
                        QuicStringPiece(packet.data(), packet.length()));
   ReceivedPacketInfo packet_info(self_address, peer_address, packet);
   std::string detailed_error;
-  const QuicErrorCode error = QuicFramer::ProcessPacketDispatcher(
-      packet, expected_server_connection_id_length_, &packet_info.form,
-      &packet_info.version_flag, &packet_info.version_label,
-      &packet_info.destination_connection_id, &packet_info.source_connection_id,
-      &detailed_error);
+  QuicErrorCode error;
+  if (!GetQuicReloadableFlag(quic_use_parse_public_header)) {
+    error = QuicFramer::ProcessPacketDispatcher(
+        packet, expected_server_connection_id_length_, &packet_info.form,
+        &packet_info.version_flag, &packet_info.version_label,
+        &packet_info.destination_connection_id,
+        &packet_info.source_connection_id, &detailed_error);
+  } else {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_use_parse_public_header);
+    bool retry_token_present;
+    QuicStringPiece retry_token;
+    error = QuicFramer::ParsePublicHeaderDispatcher(
+        packet, expected_server_connection_id_length_, &packet_info.form,
+        &packet_info.version_flag, &packet_info.use_length_prefix,
+        &packet_info.version_label, &packet_info.version,
+        &packet_info.destination_connection_id,
+        &packet_info.source_connection_id, &retry_token_present, &retry_token,
+        &detailed_error);
+  }
   if (error != QUIC_NO_ERROR) {
     // Packet has framing error.
     SetLastError(error);
@@ -319,7 +333,8 @@
                       << " to time-wait list.";
       StatelesslyTerminateConnection(
           server_connection_id, packet_info.form, packet_info.version_flag,
-          packet_info.version, QUIC_HANDSHAKE_FAILED, "Reject connection",
+          packet_info.use_length_prefix, packet_info.version,
+          QUIC_HANDSHAKE_FAILED, "Reject connection",
           quic::QuicTimeWaitListManager::SEND_STATELESS_RESET);
 
       DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait(
@@ -396,9 +411,12 @@
             packet_info.source_connection_id;
         time_wait_list_manager()->SendVersionNegotiationPacket(
             server_connection_id, client_connection_id,
-            packet_info.form != GOOGLE_QUIC_PACKET, GetSupportedVersions(),
-            packet_info.self_address, packet_info.peer_address,
-            GetPerPacketContext());
+            packet_info.form != GOOGLE_QUIC_PACKET,
+            packet_info.form != GOOGLE_QUIC_PACKET &&
+                !QuicVersionLabelUses4BitConnectionIdLength(
+                    packet_info.version_label),
+            GetSupportedVersions(), packet_info.self_address,
+            packet_info.peer_address, GetPerPacketContext());
       }
       return true;
     }
@@ -454,7 +472,8 @@
       QUIC_CODE_COUNT(quic_reject_fate_time_wait);
       StatelesslyTerminateConnection(
           server_connection_id, packet_info->form, packet_info->version_flag,
-          packet_info->version, QUIC_HANDSHAKE_FAILED, "Reject connection",
+          packet_info->use_length_prefix, packet_info->version,
+          QUIC_HANDSHAKE_FAILED, "Reject connection",
           quic::QuicTimeWaitListManager::SEND_STATELESS_RESET);
 
       DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait(
@@ -521,7 +540,9 @@
           VersionHasIetfInvariantHeader(connection->transport_version())
               ? IETF_QUIC_LONG_HEADER_PACKET
               : GOOGLE_QUIC_PACKET,
-          /*version_flag=*/true, connection->version(), QUIC_HANDSHAKE_FAILED,
+          /*version_flag=*/true,
+          connection->version().HasLengthPrefixedConnectionIds(),
+          connection->version(), QUIC_HANDSHAKE_FAILED,
           "Connection is closed by server before handshake confirmed",
           // Although it is our intention to send termination packets, the
           // |action| argument is not used by this call to
@@ -665,6 +686,7 @@
     QuicConnectionId server_connection_id,
     PacketHeaderFormat format,
     bool version_flag,
+    bool use_length_prefix,
     ParsedQuicVersion version,
     QuicErrorCode error_code,
     const std::string& error_details,
@@ -707,7 +729,7 @@
   std::vector<std::unique_ptr<QuicEncryptedPacket>> termination_packets;
   termination_packets.push_back(QuicFramer::BuildVersionNegotiationPacket(
       server_connection_id, EmptyQuicConnectionId(),
-      /*ietf_quic=*/format != GOOGLE_QUIC_PACKET,
+      /*ietf_quic=*/format != GOOGLE_QUIC_PACKET, use_length_prefix,
       /*versions=*/{}));
   time_wait_list_manager()->AddConnectionIdToTimeWait(
       server_connection_id, /*ietf_quic=*/format != GOOGLE_QUIC_PACKET,
@@ -728,8 +750,10 @@
       server_connection_id,
       early_arrived_packets.ietf_quic ? IETF_QUIC_LONG_HEADER_PACKET
                                       : GOOGLE_QUIC_PACKET,
-      /*version_flag=*/true, early_arrived_packets.version,
-      QUIC_HANDSHAKE_FAILED, "Packets buffered for too long",
+      /*version_flag=*/true,
+      early_arrived_packets.version.HasLengthPrefixedConnectionIds(),
+      early_arrived_packets.version, QUIC_HANDSHAKE_FAILED,
+      "Packets buffered for too long",
       quic::QuicTimeWaitListManager::SEND_STATELESS_RESET);
 }
 
@@ -820,7 +844,8 @@
     QUIC_CODE_COUNT(quic_reject_stop_accepting_new_connections);
     StatelesslyTerminateConnection(
         packet_info->destination_connection_id, packet_info->form,
-        /*version_flag=*/true, packet_info->version, QUIC_HANDSHAKE_FAILED,
+        /*version_flag=*/true, packet_info->use_length_prefix,
+        packet_info->version, QUIC_HANDSHAKE_FAILED,
         "Stop accepting new connections",
         quic::QuicTimeWaitListManager::SEND_STATELESS_RESET);
     // Time wait list will reject the packet correspondingly.
diff --git a/quic/core/quic_dispatcher.h b/quic/core/quic_dispatcher.h
index cfaafa0..091e727 100644
--- a/quic/core/quic_dispatcher.h
+++ b/quic/core/quic_dispatcher.h
@@ -254,6 +254,7 @@
       QuicConnectionId server_connection_id,
       PacketHeaderFormat format,
       bool version_flag,
+      bool use_length_prefix,
       ParsedQuicVersion version,
       QuicErrorCode error_code,
       const std::string& error_details,
diff --git a/quic/core/quic_dispatcher_test.cc b/quic/core/quic_dispatcher_test.cc
index b18bddb..0ac5d56 100644
--- a/quic/core/quic_dispatcher_test.cc
+++ b/quic/core/quic_dispatcher_test.cc
@@ -524,13 +524,14 @@
 }
 
 TEST_F(QuicDispatcherTest, StatelessVersionNegotiation) {
+  SetQuicReloadableFlag(quic_use_parse_public_header, true);
   CreateTimeWaitListManager();
   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
 
   EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0);
   EXPECT_CALL(
       *time_wait_list_manager_,
-      SendVersionNegotiationPacket(TestConnectionId(1), _, _, _, _, _, _))
+      SendVersionNegotiationPacket(TestConnectionId(1), _, _, _, _, _, _, _))
       .Times(1);
   // Pad the CHLO message with enough data to make the packet large enough
   // to trigger version negotiation.
@@ -543,13 +544,14 @@
 
 TEST_F(QuicDispatcherTest, StatelessVersionNegotiationWithClientConnectionId) {
   SetQuicRestartFlag(quic_do_not_override_connection_id, true);
+  SetQuicReloadableFlag(quic_use_parse_public_header, true);
   CreateTimeWaitListManager();
   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
 
   EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0);
   EXPECT_CALL(*time_wait_list_manager_,
-              SendVersionNegotiationPacket(TestConnectionId(1),
-                                           TestConnectionId(2), _, _, _, _, _))
+              SendVersionNegotiationPacket(
+                  TestConnectionId(1), TestConnectionId(2), _, _, _, _, _, _))
       .Times(1);
   // Pad the CHLO message with enough data to make the packet large enough
   // to trigger version negotiation.
@@ -567,7 +569,7 @@
 
   EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0);
   EXPECT_CALL(*time_wait_list_manager_,
-              SendVersionNegotiationPacket(_, _, _, _, _, _, _))
+              SendVersionNegotiationPacket(_, _, _, _, _, _, _, _))
       .Times(0);
   std::string chlo = SerializeCHLO() + std::string(1200, 'a');
   // Truncate to 1100 bytes of payload which results in a packet just
@@ -583,6 +585,7 @@
 // Disabling CHLO size validation allows the dispatcher to send version
 // negotiation packets in response to a CHLO that is otherwise too small.
 TEST_F(QuicDispatcherTest, VersionNegotiationWithoutChloSizeValidation) {
+  SetQuicReloadableFlag(quic_use_parse_public_header, true);
   crypto_config_.set_validate_chlo_size(false);
 
   CreateTimeWaitListManager();
@@ -590,7 +593,7 @@
 
   EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0);
   EXPECT_CALL(*time_wait_list_manager_,
-              SendVersionNegotiationPacket(_, _, _, _, _, _, _))
+              SendVersionNegotiationPacket(_, _, _, _, _, _, _, _))
       .Times(1);
   std::string chlo = SerializeCHLO() + std::string(1200, 'a');
   // Truncate to 1100 bytes of payload which results in a packet just
@@ -927,6 +930,8 @@
 }
 
 TEST_F(QuicDispatcherTest, SupportedTransportVersionsChangeInFlight) {
+  SetQuicRestartFlag(quic_dispatcher_hands_chlo_extractor_one_version, true);
+  SetQuicReloadableFlag(quic_use_parse_public_header, true);
   static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 7u,
                 "Supported versions out of sync");
   SetQuicReloadableFlag(quic_disable_version_39, false);
@@ -994,7 +999,7 @@
                             QuicTime::Zero());
   EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0);
   EXPECT_CALL(*time_wait_list_manager_,
-              SendVersionNegotiationPacket(_, _, _, _, _, _, _))
+              SendVersionNegotiationPacket(_, _, _, _, _, _, _, _))
       .Times(1);
   dispatcher_->ProcessPacket(server_address_, client_address, packet);
 }
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc
index e9d6fa9..56e7d28 100644
--- a/quic/core/quic_framer.cc
+++ b/quic/core/quic_framer.cc
@@ -408,6 +408,7 @@
 }
 
 bool AppendIetfConnectionIds(bool version_flag,
+                             bool use_length_prefix,
                              QuicConnectionId destination_connection_id,
                              QuicConnectionId source_connection_id,
                              QuicDataWriter* writer) {
@@ -415,6 +416,11 @@
     return writer->WriteConnectionId(destination_connection_id);
   }
 
+  if (use_length_prefix) {
+    return writer->WriteLengthPrefixedConnectionId(destination_connection_id) &&
+           writer->WriteLengthPrefixedConnectionId(source_connection_id);
+  }
+
   // Compute connection ID length byte.
   uint8_t dcil = GetConnectionIdLengthValue(
       static_cast<QuicConnectionIdLength>(destination_connection_id.length()));
@@ -1399,6 +1405,7 @@
     QuicConnectionId server_connection_id,
     QuicConnectionId client_connection_id,
     bool ietf_quic,
+    bool use_length_prefix,
     const ParsedQuicVersionVector& versions) {
   ParsedQuicVersionVector wire_versions = versions;
   if (!GetQuicReloadableFlag(quic_version_negotiation_grease)) {
@@ -1431,11 +1438,14 @@
   }
   if (ietf_quic) {
     return BuildIetfVersionNegotiationPacket(
-        server_connection_id, client_connection_id, wire_versions);
+        use_length_prefix, server_connection_id, client_connection_id,
+        wire_versions);
   }
 
   // The GQUIC encoding does not support encoding client connection IDs.
   DCHECK(client_connection_id.IsEmpty());
+  // The GQUIC encoding does not support length-prefixed connection IDs.
+  DCHECK(!use_length_prefix);
 
   DCHECK(!wire_versions.empty());
   size_t len = kPublicFlagsSize + server_connection_id.length() +
@@ -1467,10 +1477,15 @@
 // static
 std::unique_ptr<QuicEncryptedPacket>
 QuicFramer::BuildIetfVersionNegotiationPacket(
+    bool use_length_prefix,
     QuicConnectionId server_connection_id,
     QuicConnectionId client_connection_id,
     const ParsedQuicVersionVector& versions) {
-  QUIC_DVLOG(1) << "Building IETF version negotiation packet: "
+  QUIC_DVLOG(1) << "Building IETF version negotiation packet with"
+                << (use_length_prefix ? "" : "out")
+                << " length prefix, server_connection_id "
+                << server_connection_id << " client_connection_id "
+                << client_connection_id << " versions "
                 << ParsedQuicVersionVectorToString(versions);
   DCHECK(client_connection_id.IsEmpty() ||
          GetQuicRestartFlag(quic_do_not_override_connection_id));
@@ -1478,6 +1493,11 @@
   size_t len = kPacketHeaderTypeSize + kConnectionIdLengthSize +
                client_connection_id.length() + server_connection_id.length() +
                (versions.size() + 1) * kQuicVersionSize;
+  if (use_length_prefix) {
+    // When using length-prefixed connection IDs, packets carry two lengths
+    // instead of one.
+    len += kConnectionIdLengthSize;
+  }
   std::unique_ptr<char[]> buffer(new char[len]);
   QuicDataWriter writer(len, buffer.get());
 
@@ -1491,8 +1511,8 @@
     return nullptr;
   }
 
-  if (!AppendIetfConnectionIds(true, client_connection_id, server_connection_id,
-                               &writer)) {
+  if (!AppendIetfConnectionIds(true, use_length_prefix, client_connection_id,
+                               server_connection_id, &writer)) {
     return nullptr;
   }
 
@@ -1630,17 +1650,26 @@
                                     const QuicPacketHeader& header) {
   DCHECK_EQ(Perspective::IS_CLIENT, perspective_);
 
-  // Parse Original Destination Connection ID Length.
-  uint8_t odcil = header.type_byte & 0xf;
-  if (odcil != 0) {
-    odcil += kConnectionIdLengthAdjustment;
-  }
-
-  // Parse Original Destination Connection ID.
   QuicConnectionId original_destination_connection_id;
-  if (!reader->ReadConnectionId(&original_destination_connection_id, odcil)) {
-    set_detailed_error("Unable to read Original Destination ConnectionId.");
-    return false;
+  if (version_.HasLengthPrefixedConnectionIds()) {
+    // Parse Original Destination Connection ID.
+    if (!reader->ReadLengthPrefixedConnectionId(
+            &original_destination_connection_id)) {
+      set_detailed_error("Unable to read Original Destination ConnectionId.");
+      return false;
+    }
+  } else {
+    // Parse Original Destination Connection ID Length.
+    uint8_t odcil = header.type_byte & 0xf;
+    if (odcil != 0) {
+      odcil += kConnectionIdLengthAdjustment;
+    }
+
+    // Parse Original Destination Connection ID.
+    if (!reader->ReadConnectionId(&original_destination_connection_id, odcil)) {
+      set_detailed_error("Unable to read Original Destination ConnectionId.");
+      return false;
+    }
   }
 
   QuicStringPiece retry_token = reader->ReadRemainingPayload();
@@ -1649,42 +1678,6 @@
   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.
@@ -1769,8 +1762,6 @@
                                        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 &&
@@ -1787,10 +1778,6 @@
     }
   }
 
-  if (!MaybeProcessIetfInitialRetryToken(encrypted_reader, header)) {
-    return false;
-  }
-
   if (!MaybeProcessIetfLength(encrypted_reader, header)) {
     return false;
   }
@@ -2218,7 +2205,7 @@
 
   // Append connection ID.
   if (!AppendIetfConnectionIds(
-          header.version_flag,
+          header.version_flag, version_.HasLengthPrefixedConnectionIds(),
           header.destination_connection_id_included != CONNECTION_ID_ABSENT
               ? header.destination_connection_id
               : EmptyQuicConnectionId(),
@@ -2550,7 +2537,7 @@
                                            QuicPacketHeader* header) {
   uint8_t type;
   if (!reader->ReadBytes(&type, 1)) {
-    set_detailed_error("Unable to read type.");
+    set_detailed_error("Unable to read first byte.");
     return false;
   }
   header->type_byte = type;
@@ -2704,6 +2691,78 @@
 
 bool QuicFramer::ProcessIetfPacketHeader(QuicDataReader* reader,
                                          QuicPacketHeader* header) {
+  if (version_.HasLengthPrefixedConnectionIds()) {
+    uint8_t expected_destination_connection_id_length =
+        perspective_ == Perspective::IS_CLIENT
+            ? expected_client_connection_id_length_
+            : expected_server_connection_id_length_;
+    QuicVersionLabel version_label;
+    bool has_length_prefix;
+    std::string detailed_error;
+    QuicErrorCode parse_result = QuicFramer::ParsePublicHeader(
+        reader, expected_destination_connection_id_length,
+        VersionHasIetfInvariantHeader(version_.transport_version),
+        &header->type_byte, &header->form, &header->version_flag,
+        &has_length_prefix, &version_label, &header->version,
+        &header->destination_connection_id, &header->source_connection_id,
+        &header->long_packet_type, &header->retry_token_length_length,
+        &header->retry_token, &detailed_error);
+    if (parse_result != QUIC_NO_ERROR) {
+      set_detailed_error(detailed_error);
+      return false;
+    }
+    header->destination_connection_id_included = CONNECTION_ID_PRESENT;
+    header->source_connection_id_included =
+        header->version_flag ? CONNECTION_ID_PRESENT : CONNECTION_ID_ABSENT;
+    if (header->source_connection_id_included == CONNECTION_ID_ABSENT) {
+      DCHECK(header->source_connection_id.IsEmpty());
+      if (perspective_ == Perspective::IS_CLIENT) {
+        header->source_connection_id = last_serialized_server_connection_id_;
+      } else {
+        header->source_connection_id = last_serialized_client_connection_id_;
+      }
+    }
+    if (header->version_flag &&
+        header->version.transport_version > QUIC_VERSION_44 &&
+        !(header->type_byte & FLAGS_FIXED_BIT)) {
+      set_detailed_error("Fixed bit is 0 in long header.");
+      return false;
+    }
+    if (!header->version_flag && version_.transport_version > QUIC_VERSION_44 &&
+        !(header->type_byte & FLAGS_FIXED_BIT)) {
+      set_detailed_error("Fixed bit is 0 in short header.");
+      return false;
+    }
+    if (!header->version_flag) {
+      if (!version_.HasHeaderProtection() &&
+          !GetShortHeaderPacketNumberLength(
+              transport_version(), header->type_byte,
+              infer_packet_header_type_from_version_,
+              &header->packet_number_length)) {
+        set_detailed_error("Failed to get short header packet number length.");
+        return false;
+      }
+      return true;
+    }
+    if (header->long_packet_type == RETRY) {
+      if (!version().SupportsRetry()) {
+        set_detailed_error("RETRY not supported in this version.");
+        return false;
+      }
+      if (perspective_ == Perspective::IS_SERVER) {
+        set_detailed_error("Client-initiated RETRY is invalid.");
+        return false;
+      }
+      return true;
+    }
+    if (!header->version.HasHeaderProtection()) {
+      header->packet_number_length = GetLongHeaderPacketNumberLength(
+          header->version.transport_version, header->type_byte);
+    }
+
+    return true;
+  }
+
   if (!ProcessIetfHeaderTypeByte(reader, header)) {
     return false;
   }
@@ -2737,13 +2796,13 @@
   // Read connection ID.
   if (!reader->ReadConnectionId(&header->destination_connection_id,
                                 destination_connection_id_length)) {
-    set_detailed_error("Unable to read Destination ConnectionId.");
+    set_detailed_error("Unable to read destination connection ID.");
     return false;
   }
 
   if (!reader->ReadConnectionId(&header->source_connection_id,
                                 source_connection_id_length)) {
-    set_detailed_error("Unable to read Source ConnectionId.");
+    set_detailed_error("Unable to read source connection ID.");
     return false;
   }
 
@@ -6208,6 +6267,7 @@
     QuicConnectionId* destination_connection_id,
     QuicConnectionId* source_connection_id,
     std::string* detailed_error) {
+  DCHECK(!GetQuicReloadableFlag(quic_use_parse_public_header));
   QuicDataReader reader(packet.data(), packet.length());
 
   *source_connection_id = EmptyQuicConnectionId();
@@ -6280,6 +6340,262 @@
 }
 
 // static
+QuicErrorCode QuicFramer::ParsePublicHeaderDispatcher(
+    const QuicEncryptedPacket& packet,
+    uint8_t expected_destination_connection_id_length,
+    PacketHeaderFormat* format,
+    bool* version_present,
+    bool* has_length_prefix,
+    QuicVersionLabel* version_label,
+    ParsedQuicVersion* parsed_version,
+    QuicConnectionId* destination_connection_id,
+    QuicConnectionId* source_connection_id,
+    bool* retry_token_present,
+    QuicStringPiece* retry_token,
+    std::string* detailed_error) {
+  QuicDataReader reader(packet.data(), packet.length());
+  if (reader.IsDoneReading()) {
+    *detailed_error = "Unable to read first byte.";
+    return QUIC_INVALID_PACKET_HEADER;
+  }
+  const uint8_t first_byte = reader.PeekByte();
+  const bool ietf_format = QuicUtils::IsIetfPacketHeader(first_byte);
+  uint8_t unused_first_byte;
+  QuicVariableLengthIntegerLength retry_token_length_length;
+  QuicLongHeaderType unused_log_packet_type;
+  const QuicErrorCode error_code = ParsePublicHeader(
+      &reader, expected_destination_connection_id_length, ietf_format,
+      &unused_first_byte, format, version_present, has_length_prefix,
+      version_label, parsed_version, destination_connection_id,
+      source_connection_id, &unused_log_packet_type, &retry_token_length_length,
+      retry_token, detailed_error);
+  *retry_token_present =
+      retry_token_length_length != VARIABLE_LENGTH_INTEGER_LENGTH_0;
+  return error_code;
+}
+
+// static
+QuicErrorCode QuicFramer::ParsePublicHeaderGoogleQuic(
+    QuicDataReader* reader,
+    uint8_t* first_byte,
+    PacketHeaderFormat* format,
+    bool* version_present,
+    QuicVersionLabel* version_label,
+    QuicConnectionId* destination_connection_id,
+    std::string* detailed_error) {
+  *format = GOOGLE_QUIC_PACKET;
+  *version_present = (*first_byte & PACKET_PUBLIC_FLAGS_VERSION) != 0;
+  uint8_t destination_connection_id_length = 0;
+  if ((*first_byte & PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID) != 0) {
+    destination_connection_id_length = kQuicDefaultConnectionIdLength;
+  }
+  if (!reader->ReadConnectionId(destination_connection_id,
+                                destination_connection_id_length)) {
+    *detailed_error = "Unable to read ConnectionId.";
+    return QUIC_INVALID_PACKET_HEADER;
+  }
+  if (*version_present && !ProcessVersionLabel(reader, version_label)) {
+    *detailed_error = "Unable to read protocol version.";
+    return QUIC_INVALID_PACKET_HEADER;
+  }
+  return QUIC_NO_ERROR;
+}
+
+namespace {
+
+inline bool PacketHasLengthPrefixedConnectionIds(
+    const QuicDataReader& reader,
+    ParsedQuicVersion parsed_version,
+    QuicVersionLabel version_label,
+    uint8_t first_byte) {
+  if (parsed_version.transport_version != QUIC_VERSION_UNSUPPORTED) {
+    return parsed_version.HasLengthPrefixedConnectionIds();
+  }
+
+  // Received unsupported version, check known old unsupported versions.
+  if (QuicVersionLabelUses4BitConnectionIdLength(version_label)) {
+    return false;
+  }
+
+  // Received unknown version, check connection ID length byte.
+  if (reader.IsDoneReading()) {
+    // This check is required to safely peek the connection ID length byte.
+    return true;
+  }
+  const uint8_t connection_id_length_byte = reader.PeekByte();
+
+  // Check for packets produced by older versions of
+  // QuicFramer::WriteClientVersionNegotiationProbePacket
+  if (first_byte == 0xc0 && (connection_id_length_byte & 0x0f) == 0 &&
+      connection_id_length_byte >= 0x50 && version_label == 0xcabadaba) {
+    return false;
+  }
+
+  return true;
+}
+
+inline bool ParseLongHeaderConnectionIds(
+    QuicDataReader* reader,
+    bool has_length_prefix,
+    QuicConnectionId* destination_connection_id,
+    QuicConnectionId* source_connection_id,
+    std::string* detailed_error) {
+  if (has_length_prefix) {
+    if (!reader->ReadLengthPrefixedConnectionId(destination_connection_id)) {
+      *detailed_error = "Unable to read destination connection ID.";
+      return false;
+    }
+    if (!reader->ReadLengthPrefixedConnectionId(source_connection_id)) {
+      *detailed_error = "Unable to read source connection ID.";
+      return false;
+    }
+  } else {
+    // Parse connection ID lengths.
+    uint8_t connection_id_lengths_byte;
+    if (!reader->ReadUInt8(&connection_id_lengths_byte)) {
+      *detailed_error = "Unable to read connection ID lengths.";
+      return false;
+    }
+    uint8_t destination_connection_id_length =
+        (connection_id_lengths_byte & kDestinationConnectionIdLengthMask) >> 4;
+    if (destination_connection_id_length != 0) {
+      destination_connection_id_length += kConnectionIdLengthAdjustment;
+    }
+    uint8_t source_connection_id_length =
+        connection_id_lengths_byte & kSourceConnectionIdLengthMask;
+    if (source_connection_id_length != 0) {
+      source_connection_id_length += kConnectionIdLengthAdjustment;
+    }
+
+    // Read destination connection ID.
+    if (!reader->ReadConnectionId(destination_connection_id,
+                                  destination_connection_id_length)) {
+      *detailed_error = "Unable to read destination connection ID.";
+      return false;
+    }
+
+    // Read source connection ID.
+    if (!reader->ReadConnectionId(source_connection_id,
+                                  source_connection_id_length)) {
+      *detailed_error = "Unable to read source connection ID.";
+      return false;
+    }
+  }
+  return true;
+}
+
+}  // namespace
+
+// static
+QuicErrorCode QuicFramer::ParsePublicHeader(
+    QuicDataReader* reader,
+    uint8_t expected_destination_connection_id_length,
+    bool ietf_format,
+    uint8_t* first_byte,
+    PacketHeaderFormat* format,
+    bool* version_present,
+    bool* has_length_prefix,
+    QuicVersionLabel* version_label,
+    ParsedQuicVersion* parsed_version,
+    QuicConnectionId* destination_connection_id,
+    QuicConnectionId* source_connection_id,
+    QuicLongHeaderType* long_packet_type,
+    QuicVariableLengthIntegerLength* retry_token_length_length,
+    QuicStringPiece* retry_token,
+    std::string* detailed_error) {
+  *version_present = false;
+  *has_length_prefix = false;
+  *version_label = 0;
+  *parsed_version = UnsupportedQuicVersion();
+  *source_connection_id = EmptyQuicConnectionId();
+  *long_packet_type = INVALID_PACKET_TYPE;
+  *retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_0;
+  *retry_token = QuicStringPiece();
+  *detailed_error = "";
+
+  if (!reader->ReadUInt8(first_byte)) {
+    *detailed_error = "Unable to read first byte.";
+    return QUIC_INVALID_PACKET_HEADER;
+  }
+
+  if (!ietf_format) {
+    return ParsePublicHeaderGoogleQuic(
+        reader, first_byte, format, version_present, version_label,
+        destination_connection_id, detailed_error);
+  }
+
+  *format = GetIetfPacketHeaderFormat(*first_byte);
+
+  if (*format == IETF_QUIC_SHORT_HEADER_PACKET) {
+    // Read destination connection ID using
+    // expected_destination_connection_id_length to determine its length.
+    if (!reader->ReadConnectionId(destination_connection_id,
+                                  expected_destination_connection_id_length)) {
+      *detailed_error = "Unable to read destination connection ID.";
+      return QUIC_INVALID_PACKET_HEADER;
+    }
+    return QUIC_NO_ERROR;
+  }
+
+  DCHECK_EQ(IETF_QUIC_LONG_HEADER_PACKET, *format);
+  *version_present = true;
+  if (!ProcessVersionLabel(reader, version_label)) {
+    *detailed_error = "Unable to read protocol version.";
+    return QUIC_INVALID_PACKET_HEADER;
+  }
+
+  if (*version_label == 0) {
+    *long_packet_type = VERSION_NEGOTIATION;
+  }
+
+  // Parse version.
+  *parsed_version = ParseQuicVersionLabel(*version_label);
+
+  // Figure out which IETF QUIC invariants this packet follows.
+  *has_length_prefix = PacketHasLengthPrefixedConnectionIds(
+      *reader, *parsed_version, *version_label, *first_byte);
+
+  // Parse connection IDs.
+  if (!ParseLongHeaderConnectionIds(reader, *has_length_prefix,
+                                    destination_connection_id,
+                                    source_connection_id, detailed_error)) {
+    return QUIC_INVALID_PACKET_HEADER;
+  }
+
+  if (parsed_version->transport_version == QUIC_VERSION_UNSUPPORTED) {
+    // Skip parsing of long packet type and retry token for unknown versions.
+    return QUIC_NO_ERROR;
+  }
+
+  // Parse long packet type.
+  if (!GetLongHeaderType(parsed_version->transport_version, *first_byte,
+                         long_packet_type)) {
+    *detailed_error = "Unable to parse long packet type.";
+    return QUIC_INVALID_PACKET_HEADER;
+  }
+
+  if (!parsed_version->SupportsRetry() || *long_packet_type != INITIAL) {
+    // Retry token is only present on initial packets for some versions.
+    return QUIC_NO_ERROR;
+  }
+
+  *retry_token_length_length = reader->PeekVarInt62Length();
+  uint64_t retry_token_length;
+  if (!reader->ReadVarInt62(&retry_token_length)) {
+    *retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_0;
+    *detailed_error = "Unable to read retry token length.";
+    return QUIC_INVALID_PACKET_HEADER;
+  }
+
+  if (!reader->ReadStringPiece(retry_token, retry_token_length)) {
+    *detailed_error = "Unable to read retry token.";
+    return QUIC_INVALID_PACKET_HEADER;
+  }
+
+  return QUIC_NO_ERROR;
+}
+
+// static
 bool QuicFramer::WriteClientVersionNegotiationProbePacket(
     char* packet_bytes,
     QuicByteCount packet_length,
@@ -6300,14 +6616,17 @@
     QUIC_BUG << "Invalid connection_id_length";
     return false;
   }
+  const bool use_length_prefix =
+      GetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids);
+  const uint8_t last_version_byte = use_length_prefix ? 0xda : 0xba;
   // clang-format off
-  static const unsigned char packet_start_bytes[] = {
+  const unsigned char packet_start_bytes[] = {
     // IETF long header with fixed bit set, type initial, all-0 encrypted bits.
     0xc0,
     // Version, part of the IETF space reserved for negotiation.
     // This intentionally differs from QuicVersionReservedForNegotiation()
     // to allow differentiating them over the wire.
-    0xca, 0xba, 0xda, 0xba,
+    0xca, 0xba, 0xda, last_version_byte,
   };
   // clang-format on
   static_assert(sizeof(packet_start_bytes) == 5, "bad packet_start_bytes size");
@@ -6319,8 +6638,9 @@
 
   QuicConnectionId destination_connection_id(destination_connection_id_bytes,
                                              destination_connection_id_length);
-  if (!AppendIetfConnectionIds(/*version_flag=*/true, destination_connection_id,
-                               EmptyQuicConnectionId(), &writer)) {
+  if (!AppendIetfConnectionIds(
+          /*version_flag=*/true, use_length_prefix, destination_connection_id,
+          EmptyQuicConnectionId(), &writer)) {
     QUIC_BUG << "Failed to write connection IDs";
     return false;
   }
@@ -6404,35 +6724,50 @@
     *detailed_error = "Packet is not a version negotiation packet";
     return false;
   }
-  uint8_t expected_server_connection_id_length = 0,
-          destination_connection_id_length = 0, source_connection_id_length = 0;
-  if (!ProcessAndValidateIetfConnectionIdLength(
-          &reader, UnsupportedQuicVersion(), Perspective::IS_CLIENT,
-          /*should_update_expected_server_connection_id_length=*/true,
-          &expected_server_connection_id_length,
-          &destination_connection_id_length, &source_connection_id_length,
-          detailed_error)) {
-    return false;
-  }
-  if (destination_connection_id_length != 0) {
-    *detailed_error = "Received unexpected destination connection ID length";
-    return false;
-  }
+  const bool use_length_prefix =
+      GetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids);
   QuicConnectionId destination_connection_id, source_connection_id;
-  if (!reader.ReadConnectionId(&destination_connection_id,
-                               destination_connection_id_length)) {
-    *detailed_error = "Failed to read destination connection ID";
-    return false;
+  if (use_length_prefix) {
+    if (!reader.ReadLengthPrefixedConnectionId(&destination_connection_id)) {
+      *detailed_error = "Failed to read destination connection ID";
+      return false;
+    }
+    if (!reader.ReadLengthPrefixedConnectionId(&source_connection_id)) {
+      *detailed_error = "Failed to read source connection ID";
+      return false;
+    }
+  } else {
+    uint8_t expected_server_connection_id_length = 0,
+            destination_connection_id_length = 0,
+            source_connection_id_length = 0;
+    if (!ProcessAndValidateIetfConnectionIdLength(
+            &reader, UnsupportedQuicVersion(), Perspective::IS_CLIENT,
+            /*should_update_expected_server_connection_id_length=*/true,
+            &expected_server_connection_id_length,
+            &destination_connection_id_length, &source_connection_id_length,
+            detailed_error)) {
+      return false;
+    }
+    if (!reader.ReadConnectionId(&destination_connection_id,
+                                 destination_connection_id_length)) {
+      *detailed_error = "Failed to read destination connection ID";
+      return false;
+    }
+    if (!reader.ReadConnectionId(&source_connection_id,
+                                 source_connection_id_length)) {
+      *detailed_error = "Failed to read source connection ID";
+      return false;
+    }
   }
-  if (!reader.ReadConnectionId(&source_connection_id,
-                               source_connection_id_length)) {
-    *detailed_error = "Failed to read source connection ID";
+
+  if (destination_connection_id.length() != 0) {
+    *detailed_error = "Received unexpected destination connection ID length";
     return false;
   }
 
   memcpy(source_connection_id_bytes, source_connection_id.data(),
-         source_connection_id_length);
-  *source_connection_id_length_out = source_connection_id_length;
+         source_connection_id.length());
+  *source_connection_id_length_out = source_connection_id.length();
 
   return true;
 }
diff --git a/quic/core/quic_framer.h b/quic/core/quic_framer.h
index 2349a77..f19c633 100644
--- a/quic/core/quic_framer.h
+++ b/quic/core/quic_framer.h
@@ -387,6 +387,43 @@
       QuicConnectionId* source_connection_id,
       std::string* detailed_error);
 
+  // Parses the unencryoted fields in a QUIC header using |reader| as input,
+  // stores the result in the other parameters.
+  // |expected_destination_connection_id_length| is only used for short headers.
+  static QuicErrorCode ParsePublicHeader(
+      QuicDataReader* reader,
+      uint8_t expected_destination_connection_id_length,
+      bool ietf_format,
+      uint8_t* first_byte,
+      PacketHeaderFormat* format,
+      bool* version_present,
+      bool* has_length_prefix,
+      QuicVersionLabel* version_label,
+      ParsedQuicVersion* parsed_version,
+      QuicConnectionId* destination_connection_id,
+      QuicConnectionId* source_connection_id,
+      QuicLongHeaderType* long_packet_type,
+      QuicVariableLengthIntegerLength* retry_token_length_length,
+      QuicStringPiece* retry_token,
+      std::string* detailed_error);
+
+  // Parses the unencryoted fields in |packet| and stores them in the other
+  // parameters. This can only be called on the server.
+  // |expected_destination_connection_id_length| is only used for short headers.
+  static QuicErrorCode ParsePublicHeaderDispatcher(
+      const QuicEncryptedPacket& packet,
+      uint8_t expected_destination_connection_id_length,
+      PacketHeaderFormat* format,
+      bool* version_present,
+      bool* has_length_prefix,
+      QuicVersionLabel* version_label,
+      ParsedQuicVersion* parsed_version,
+      QuicConnectionId* destination_connection_id,
+      QuicConnectionId* source_connection_id,
+      bool* retry_token_present,
+      QuicStringPiece* retry_token,
+      std::string* detailed_error);
+
   // Serializes a packet containing |frames| into |buffer|.
   // Returns the length of the packet, which must not be longer than
   // |packet_length|.  Returns 0 if it fails to serialize.
@@ -437,10 +474,12 @@
       QuicConnectionId server_connection_id,
       QuicConnectionId client_connection_id,
       bool ietf_quic,
+      bool use_length_prefix,
       const ParsedQuicVersionVector& versions);
 
   // Returns a new IETF version negotiation packet.
   static std::unique_ptr<QuicEncryptedPacket> BuildIetfVersionNegotiationPacket(
+      bool use_length_prefix,
       QuicConnectionId server_connection_id,
       QuicConnectionId client_connection_id,
       const ParsedQuicVersionVector& versions);
@@ -678,9 +717,6 @@
   bool ProcessRetryPacket(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);
@@ -816,6 +852,15 @@
 
   static AckFrameInfo GetAckFrameInfo(const QuicAckFrame& frame);
 
+  static QuicErrorCode ParsePublicHeaderGoogleQuic(
+      QuicDataReader* reader,
+      uint8_t* first_byte,
+      PacketHeaderFormat* format,
+      bool* version_present,
+      QuicVersionLabel* version_label,
+      QuicConnectionId* destination_connection_id,
+      std::string* detailed_error);
+
   // The Append* methods attempt to write the provided header or frame using the
   // |writer|, and return true if successful.
 
@@ -945,6 +990,7 @@
   void set_error(QuicErrorCode error) { error_ = error; }
 
   void set_detailed_error(const char* error) { detailed_error_ = error; }
+  void set_detailed_error(std::string error) { detailed_error_ = error; }
 
   std::string detailed_error_;
   QuicFramerVisitorInterface* visitor_;
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc
index d73c412..e2c106a 100644
--- a/quic/core/quic_framer_test.cc
+++ b/quic/core/quic_framer_test.cc
@@ -218,7 +218,7 @@
     QUIC_DLOG(INFO) << "QuicFramer Version Mismatch, version: "
                     << received_version;
     ++version_mismatch_;
-    return true;
+    return false;
   }
 
   bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override {
@@ -917,11 +917,25 @@
   QuicConnectionId destination_connection_id, source_connection_id;
   QuicVersionLabel version_label;
   std::string detailed_error;
-  EXPECT_EQ(QUIC_NO_ERROR,
-            QuicFramer::ProcessPacketDispatcher(
-                *encrypted, kQuicDefaultConnectionIdLength, &format,
-                &version_flag, &version_label, &destination_connection_id,
-                &source_connection_id, &detailed_error));
+  QuicErrorCode error_code;
+  if (!GetQuicReloadableFlag(quic_use_parse_public_header)) {
+    error_code = QuicFramer::ProcessPacketDispatcher(
+        *encrypted, kQuicDefaultConnectionIdLength, &format, &version_flag,
+        &version_label, &destination_connection_id, &source_connection_id,
+        &detailed_error);
+  } else {
+    bool retry_token_present, use_length_prefix;
+    QuicStringPiece retry_token;
+    ParsedQuicVersion parsed_version = UnsupportedQuicVersion();
+    error_code = QuicFramer::ParsePublicHeaderDispatcher(
+        *encrypted, kQuicDefaultConnectionIdLength, &format, &version_flag,
+        &use_length_prefix, &version_label, &parsed_version,
+        &destination_connection_id, &source_connection_id, &retry_token_present,
+        &retry_token, &detailed_error);
+    EXPECT_FALSE(retry_token_present);
+    EXPECT_FALSE(use_length_prefix);
+  }
+  EXPECT_EQ(QUIC_NO_ERROR, error_code);
   EXPECT_EQ(GOOGLE_QUIC_PACKET, format);
   EXPECT_FALSE(version_flag);
   EXPECT_EQ(kQuicDefaultConnectionIdLength, destination_connection_id.length());
@@ -933,7 +947,7 @@
   // clang-format off
   PacketFragments packet44 = {
     // type (long header with packet type INITIAL)
-    {"Unable to read type.",
+    {"Unable to read first byte.",
      {0xFF}},
     // version tag
     {"Unable to read protocol version.",
@@ -942,7 +956,7 @@
     {"Unable to read ConnectionId length.",
      {0x50}},
     // connection_id
-    {"Unable to read Destination ConnectionId.",
+    {"Unable to read destination connection ID.",
      {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
     // packet number
     {"Unable to read packet number.",
@@ -950,7 +964,7 @@
   };
   PacketFragments packet46 = {
     // type (long header with packet type INITIAL)
-    {"Unable to read type.",
+    {"Unable to read first byte.",
      {0xC3}},
     // version tag
     {"Unable to read protocol version.",
@@ -959,7 +973,7 @@
     {"Unable to read ConnectionId length.",
      {0x50}},
     // connection_id
-    {"Unable to read Destination ConnectionId.",
+    {"Unable to read destination connection ID.",
      {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
     // packet number
     {"Unable to read packet number.",
@@ -995,11 +1009,24 @@
   QuicConnectionId destination_connection_id, source_connection_id;
   QuicVersionLabel version_label;
   std::string detailed_error;
-  EXPECT_EQ(QUIC_NO_ERROR,
-            QuicFramer::ProcessPacketDispatcher(
-                *encrypted, kQuicDefaultConnectionIdLength, &format,
-                &version_flag, &version_label, &destination_connection_id,
-                &source_connection_id, &detailed_error));
+  QuicErrorCode error_code;
+  if (!GetQuicReloadableFlag(quic_use_parse_public_header)) {
+    error_code = QuicFramer::ProcessPacketDispatcher(
+        *encrypted, kQuicDefaultConnectionIdLength, &format, &version_flag,
+        &version_label, &destination_connection_id, &source_connection_id,
+        &detailed_error);
+  } else {
+    bool retry_token_present, use_length_prefix;
+    QuicStringPiece retry_token;
+    ParsedQuicVersion parsed_version = UnsupportedQuicVersion();
+    error_code = QuicFramer::ParsePublicHeaderDispatcher(
+        *encrypted, kQuicDefaultConnectionIdLength, &format, &version_flag,
+        &use_length_prefix, &version_label, &parsed_version,
+        &destination_connection_id, &source_connection_id, &retry_token_present,
+        &retry_token, &detailed_error);
+    EXPECT_EQ(retry_token_present, framer_.version().SupportsRetry());
+    EXPECT_FALSE(use_length_prefix);
+  }
   EXPECT_EQ(IETF_QUIC_LONG_HEADER_PACKET, format);
   EXPECT_TRUE(version_flag);
   EXPECT_EQ(kQuicDefaultConnectionIdLength, destination_connection_id.length());
@@ -1012,6 +1039,7 @@
     // This test requires an IETF long header.
     return;
   }
+  SetQuicReloadableFlag(quic_use_parse_public_header, false);
   SetQuicRestartFlag(quic_do_not_override_connection_id, true);
   SetDecrypterLevel(ENCRYPTION_ZERO_RTT);
   const unsigned char type_byte =
@@ -1044,11 +1072,24 @@
   QuicConnectionId destination_connection_id, source_connection_id;
   QuicVersionLabel version_label = 0;
   std::string detailed_error = "";
-  EXPECT_EQ(QUIC_NO_ERROR,
-            QuicFramer::ProcessPacketDispatcher(
-                encrypted, kQuicDefaultConnectionIdLength, &format,
-                &version_flag, &version_label, &destination_connection_id,
-                &source_connection_id, &detailed_error));
+  QuicErrorCode error_code;
+  if (!GetQuicReloadableFlag(quic_use_parse_public_header)) {
+    error_code = QuicFramer::ProcessPacketDispatcher(
+        encrypted, kQuicDefaultConnectionIdLength, &format, &version_flag,
+        &version_label, &destination_connection_id, &source_connection_id,
+        &detailed_error);
+  } else {
+    bool retry_token_present, use_length_prefix;
+    QuicStringPiece retry_token;
+    ParsedQuicVersion parsed_version = UnsupportedQuicVersion();
+    error_code = QuicFramer::ParsePublicHeaderDispatcher(
+        encrypted, kQuicDefaultConnectionIdLength, &format, &version_flag,
+        &use_length_prefix, &version_label, &parsed_version,
+        &destination_connection_id, &source_connection_id, &retry_token_present,
+        &retry_token, &detailed_error);
+    EXPECT_FALSE(retry_token_present);
+    EXPECT_FALSE(use_length_prefix);
+  }
   EXPECT_EQ("", detailed_error);
   EXPECT_EQ(IETF_QUIC_LONG_HEADER_PACKET, format);
   EXPECT_TRUE(version_flag);
@@ -1142,7 +1183,7 @@
 
   PacketFragments packet44 = {
         // type (short header, 4 byte packet number)
-        {"Unable to read type.",
+        {"Unable to read first byte.",
          {0x32}},
         // connection_id
         // packet number
@@ -1152,7 +1193,7 @@
 
   PacketFragments packet46 = {
         // type (short header, 4 byte packet number)
-        {"Unable to read type.",
+        {"Unable to read first byte.",
          {0x43}},
         // connection_id
         // packet number
@@ -1162,7 +1203,7 @@
 
   PacketFragments packet_hp = {
         // type (short header, 4 byte packet number)
-        {"Unable to read type.",
+        {"Unable to read first byte.",
          {0x43}},
         // connection_id
         // packet number
@@ -1216,7 +1257,7 @@
 
   PacketFragments packet44 = {
       // type (long header with packet type ZERO_RTT_PROTECTED)
-      {"Unable to read type.",
+      {"Unable to read first byte.",
        {0xFC}},
       // version tag
       {"Unable to read protocol version.",
@@ -1225,7 +1266,7 @@
       {"Unable to read ConnectionId length.",
        {0x50}},
       // connection_id
-      {"Unable to read Destination ConnectionId.",
+      {"Unable to read destination connection ID.",
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"Unable to read packet number.",
@@ -1235,7 +1276,7 @@
   PacketFragments packet46 = {
       // type (long header with packet type ZERO_RTT_PROTECTED and 4 bytes
       // packet number)
-      {"Unable to read type.",
+      {"Unable to read first byte.",
        {0xD3}},
       // version tag
       {"Unable to read protocol version.",
@@ -1244,7 +1285,7 @@
       {"Unable to read ConnectionId length.",
        {0x50}},
       // connection_id
-      {"Unable to read Destination ConnectionId.",
+      {"Unable to read destination connection ID.",
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"Unable to read packet number.",
@@ -1254,17 +1295,20 @@
   PacketFragments packet99 = {
       // type (long header with packet type ZERO_RTT_PROTECTED and 4 bytes
       // packet number)
-      {"Unable to read type.",
+      {"Unable to read first byte.",
        {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.",
+      // destination connection ID length
+      {"Unable to read destination connection ID.",
+       {0x08}},
+      // destination connection ID
+      {"Unable to read destination connection ID.",
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+      // source connection ID length
+      {"Unable to read source connection ID.",
+       {0x00}},
       // long header packet length
       {"Unable to read long header payload length.",
        {0x04}},
@@ -1315,10 +1359,10 @@
 
   PacketFragments packet44 = {
       // type (short header, 4 byte packet number)
-      {"Unable to read type.",
+      {"Unable to read first byte.",
        {0x32}},
       // connection_id
-      {"Unable to read Destination ConnectionId.",
+      {"Unable to read destination connection ID.",
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"Unable to read packet number.",
@@ -1327,10 +1371,10 @@
 
   PacketFragments packet46 = {
       // type (short header, 4 byte packet number)
-      {"Unable to read type.",
+      {"Unable to read first byte.",
        {0x43}},
       // connection_id
-      {"Unable to read Destination ConnectionId.",
+      {"Unable to read destination connection ID.",
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"Unable to read packet number.",
@@ -1339,10 +1383,10 @@
 
   PacketFragments packet_hp = {
       // type (short header, 4 byte packet number)
-      {"Unable to read type.",
+      {"Unable to read first byte.",
        {0x43}},
       // connection_id
-      {"Unable to read Destination ConnectionId.",
+      {"Unable to read destination connection ID.",
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"",
@@ -1390,10 +1434,10 @@
 
   PacketFragments packet44 = {
       // type (short header, 2 byte packet number)
-      {"Unable to read type.",
+      {"Unable to read first byte.",
        {0x31}},
       // connection_id
-      {"Unable to read Destination ConnectionId.",
+      {"Unable to read destination connection ID.",
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"Unable to read packet number.",
@@ -1402,10 +1446,10 @@
 
   PacketFragments packet46 = {
       // type (short header, 2 byte packet number)
-      {"Unable to read type.",
+      {"Unable to read first byte.",
        {0x41}},
       // connection_id
-      {"Unable to read Destination ConnectionId.",
+      {"Unable to read destination connection ID.",
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"Unable to read packet number.",
@@ -1414,10 +1458,10 @@
 
   PacketFragments packet_hp = {
       // type (short header, 2 byte packet number)
-      {"Unable to read type.",
+      {"Unable to read first byte.",
        {0x41}},
       // connection_id
-      {"Unable to read Destination ConnectionId.",
+      {"Unable to read destination connection ID.",
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"",
@@ -1473,10 +1517,10 @@
 
   PacketFragments packet44 = {
       // type (8 byte connection_id and 1 byte packet number)
-      {"Unable to read type.",
+      {"Unable to read first byte.",
        {0x30}},
       // connection_id
-      {"Unable to read Destination ConnectionId.",
+      {"Unable to read destination connection ID.",
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"Unable to read packet number.",
@@ -1485,10 +1529,10 @@
 
   PacketFragments packet46 = {
       // type (8 byte connection_id and 1 byte packet number)
-      {"Unable to read type.",
+      {"Unable to read first byte.",
        {0x40}},
       // connection_id
-      {"Unable to read Destination ConnectionId.",
+      {"Unable to read destination connection ID.",
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"Unable to read packet number.",
@@ -1497,10 +1541,10 @@
 
   PacketFragments packet_hp = {
       // type (8 byte connection_id and 1 byte packet number)
-      {"Unable to read type.",
+      {"Unable to read first byte.",
        {0x40}},
       // connection_id
-      {"Unable to read Destination ConnectionId.",
+      {"Unable to read destination connection ID.",
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
       // packet number
       {"",
@@ -1677,9 +1721,11 @@
     0xD0,
     // version tag
     QUIC_VERSION_BYTES,
-    // connection_id length
-    0x05,
-    // connection_id
+    // destination connection ID length
+    0x00,
+    // source connection ID length
+    0x08,
+    // source connection ID
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
     // long header packet length
     0x26,
@@ -1775,11 +1821,33 @@
     0x00,
     0x00, 0x00, 0x00, 0x00
   };
+
+  unsigned char packet99[] = {
+    // type (long header, ZERO_RTT_PROTECTED, 4-byte packet number)
+    0xD3,
+    // version tag
+    'Q', '0', '0', '0',
+    // destination connection ID length
+    0x08,
+    // destination connection ID
+    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+    // source connection ID length
+    0x00,
+    // packet number
+    0x12, 0x34, 0x56, 0x78,
+
+    // frame type (padding frame)
+    0x00,
+    0x00, 0x00, 0x00, 0x00
+  };
   // clang-format on
 
   unsigned char* p = packet;
   size_t p_size = QUIC_ARRAYSIZE(packet);
-  if (framer_.transport_version() > QUIC_VERSION_44) {
+  if (framer_.transport_version() >= QUIC_VERSION_99) {
+    p = packet99;
+    p_size = QUIC_ARRAYSIZE(packet99);
+  } else if (framer_.transport_version() > QUIC_VERSION_44) {
     p = packet45;
     p_size = QUIC_ARRAYSIZE(packet45);
   } else if (framer_.transport_version() > QUIC_VERSION_43) {
@@ -1792,8 +1860,6 @@
   ASSERT_TRUE(visitor_.header_.get());
   EXPECT_EQ(0, visitor_.frame_count_);
   EXPECT_EQ(1, visitor_.version_mismatch_);
-  ASSERT_EQ(1u, visitor_.padding_frames_.size());
-  EXPECT_EQ(5, visitor_.padding_frames_[0]->num_padding_bytes);
 }
 
 TEST_P(QuicFramerTest, PaddingFrame) {
@@ -2213,9 +2279,11 @@
         0xD3,
         // version tag
         QUIC_VERSION_BYTES,
-        // connection_id length
-        0x05,
-        // connection_id
+        // destination connection ID length
+        0x00,
+        // source connection ID length
+        0x08,
+        // source connection ID
         0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE,
         // IETF long header payload length
         0x05,
@@ -2738,12 +2806,15 @@
       // version tag
       {"",
        {QUIC_VERSION_BYTES}},
-      // connection_id length
+      // destination connection ID length
       {"",
-       {0x50}},
-      // connection_id
+       {0x08}},
+      // destination connection ID
       {"",
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+      // source connection ID length
+      {"",
+       {0x00}},
       // long header packet length
       {"",
        {0x1E}},
@@ -5682,12 +5753,34 @@
        {QUIC_VERSION_BYTES,
         'Q', '2', '.', '0'}},
   };
+
+  PacketFragments packet99 = {
+      // type (long header)
+      {"",
+       {0x8F}},
+      // version tag
+      {"",
+       {0x00, 0x00, 0x00, 0x00}},
+      {"",
+       {0x08}},
+      // connection_id
+      {"",
+       {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+      {"",
+       {0x00}},
+      // Supported versions
+      {"Unable to read supported version in negotiation.",
+       {QUIC_VERSION_BYTES,
+        'Q', '2', '.', '0'}},
+  };
   // clang-format on
 
   QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
 
   PacketFragments& fragments =
-      framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet;
+      framer_.transport_version() >= QUIC_VERSION_99
+          ? packet99
+          : framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet;
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       AssemblePacketFromFragments(fragments));
   EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
@@ -5725,9 +5818,30 @@
       QUIC_VERSION_BYTES,
       'Q', '2', '.', '0',
   };
+  unsigned char packet2[] = {
+      // public flags (long header with all ignored bits set)
+      0xFF,
+      // version
+      0x00, 0x00, 0x00, 0x00,
+      // destination connection ID length
+      0x08,
+      // destination connection ID
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11,
+      // source connection ID length
+      0x00,
+      // supported versions
+      QUIC_VERSION_BYTES,
+      'Q', '2', '.', '0',
+  };
   // clang-format on
+  unsigned char* p = packet;
+  size_t p_length = QUIC_ARRAYSIZE(packet);
+  if (framer_.version().HasLengthPrefixedConnectionIds()) {
+    p = packet2;
+    p_length = QUIC_ARRAYSIZE(packet2);
+  }
 
-  QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false);
+  QuicEncryptedPacket encrypted(AsChars(p), p_length, false);
   EXPECT_FALSE(framer_.ProcessPacket(encrypted));
   EXPECT_EQ(QUIC_INVALID_VERSION_NEGOTIATION_PACKET, framer_.error());
   EXPECT_EQ("Server received version negotiation packet.",
@@ -5796,9 +5910,34 @@
       'H', 'e', 'l', 'l', 'o', ' ', 't', 'h', 'i', 's',
       ' ', 'i', 's', ' ', 'R', 'E', 'T', 'R', 'Y', '!',
   };
+  unsigned char packet99[] = {
+      // public flags (long header with packet type RETRY)
+      0xF0,
+      // version
+      QUIC_VERSION_BYTES,
+      // destination connection ID length
+      0x00,
+      // source connection ID length
+      0x08,
+      // source connection ID
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11,
+      // original destination connection ID length
+      0x08,
+      // original destination connection ID
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // retry token
+      'H', 'e', 'l', 'l', 'o', ' ', 't', 'h', 'i', 's',
+      ' ', 'i', 's', ' ', 'R', 'E', 'T', 'R', 'Y', '!',
+  };
   // clang-format on
 
-  QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false);
+  unsigned char* p = packet;
+  size_t p_length = QUIC_ARRAYSIZE(packet);
+  if (framer_.transport_version() == QUIC_VERSION_99) {
+    p = packet99;
+    p_length = QUIC_ARRAYSIZE(packet99);
+  }
+  QuicEncryptedPacket encrypted(AsChars(p), p_length, false);
   EXPECT_TRUE(framer_.ProcessPacket(encrypted));
 
   EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
@@ -5828,7 +5967,7 @@
       // version
       QUIC_VERSION_BYTES,
       // connection ID lengths
-      0x05,
+      0x00, 0x08,
       // source connection ID
       0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11,
       // original destination connection ID
@@ -6557,10 +6696,12 @@
       0xD3,
       // version tag
       QUIC_VERSION_BYTES,
-      // connection_id length
-      0x50,
-      // connection_id
+      // destination connection ID length
+      0x08,
+      // destination connection ID
       0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // source connection ID length
+      0x00,
       // length
       0x40, 0x1D,
       // packet number
@@ -6786,10 +6927,28 @@
       0xDA, 0x5A, 0x3A, 0x3A,
       QUIC_VERSION_BYTES,
   };
+  unsigned char packet99[] = {
+      // type (long header)
+      0xC0,
+      // version tag
+      0x00, 0x00, 0x00, 0x00,
+      // destination connection ID length
+      0x00,
+      // source connection ID length
+      0x08,
+      // source connection ID
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // supported versions
+      0xDA, 0x5A, 0x3A, 0x3A,
+      QUIC_VERSION_BYTES,
+  };
   // clang-format on
   unsigned char* p = packet;
   size_t p_size = QUIC_ARRAYSIZE(packet);
-  if (framer_.transport_version() > QUIC_VERSION_43) {
+  if (framer_.transport_version() >= QUIC_VERSION_99) {
+    p = packet99;
+    p_size = QUIC_ARRAYSIZE(packet99);
+  } else if (framer_.transport_version() > QUIC_VERSION_43) {
     p = packet44;
     p_size = QUIC_ARRAYSIZE(packet44);
   }
@@ -6799,14 +6958,14 @@
       QuicFramer::BuildVersionNegotiationPacket(
           connection_id, EmptyQuicConnectionId(),
           framer_.transport_version() > QUIC_VERSION_43,
+          framer_.version().HasLengthPrefixedConnectionIds(),
           SupportedVersions(GetParam())));
   test::CompareCharArraysWithHexError("constructed packet", data->data(),
                                       data->length(), AsChars(p), p_size);
 }
 
 TEST_P(QuicFramerTest, BuildVersionNegotiationPacketWithClientConnectionId) {
-  if (framer_.transport_version() <= QUIC_VERSION_43) {
-    // The GQUIC encoding does not support encoding client connection IDs.
+  if (!framer_.version().SupportsClientConnectionIds()) {
     return;
   }
 
@@ -6821,11 +6980,11 @@
       0xC0,
       // version tag
       0x00, 0x00, 0x00, 0x00,
-      // connection ID lengths
-      0x55,
       // client/destination connection ID
+      0x08,
       0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11,
       // server/source connection ID
+      0x08,
       0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
       // supported versions
       0xDA, 0x5A, 0x3A, 0x3A,
@@ -6836,9 +6995,9 @@
   QuicConnectionId server_connection_id = FramerTestConnectionId();
   QuicConnectionId client_connection_id = FramerTestConnectionIdPlusOne();
   std::unique_ptr<QuicEncryptedPacket> data(
-      QuicFramer::BuildVersionNegotiationPacket(server_connection_id,
-                                                client_connection_id, true,
-                                                SupportedVersions(GetParam())));
+      QuicFramer::BuildVersionNegotiationPacket(
+          server_connection_id, client_connection_id, true, true,
+          SupportedVersions(GetParam())));
   test::CompareCharArraysWithHexError("constructed packet", data->data(),
                                       data->length(), AsChars(packet),
                                       QUIC_ARRAYSIZE(packet));
@@ -9873,10 +10032,12 @@
     0xD3,
     // version tag
     'Q', '.', '1', '0',
-    // connection_id length
-    0x50,
-    // connection_id
+    // destination connection ID length
+    0x08,
+    // destination connection ID
     0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+    // source connection ID length
+    0x00,
     // packet number
     0x12, 0x34, 0x56, 0x78,
 
@@ -13051,9 +13212,11 @@
       // version
       QUIC_VERSION_BYTES,
       // destination connection ID length
-      0x50,
+      0x08,
       // destination connection ID
       0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // source connection ID length
+      0x00,
       // long header packet length
       0x1E,
       // packet number
@@ -13078,9 +13241,11 @@
       // version
       QUIC_VERSION_BYTES,
       // destination connection ID length
-      0x50,
+      0x08,
       // destination connection ID
       0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // source connection ID length
+      0x00,
       // long header packet length
       0x1E,
       // packet number
@@ -13146,9 +13311,11 @@
       // version
       QUIC_VERSION_BYTES,
       // destination connection ID length
-      0x50,
+      0x08,
       // destination connection ID
       0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // source connection ID length
+      0x00,
       // long header packet length
       0x1E,
       // packet number
@@ -13173,9 +13340,11 @@
       // version
       QUIC_VERSION_BYTES,
       // destination connection ID length
-      0x50,
+      0x08,
       // destination connection ID
       0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11,
+      // source connection ID length
+      0x00,
       // long header packet length
       0x1E,
       // packet number
@@ -13229,9 +13398,11 @@
       // version
       QUIC_VERSION_BYTES,
       // destination connection ID length
-      0x50,
+      0x08,
       // destination connection ID
       0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+      // source connection ID length
+      0x00,
       // long header packet length
       0x1E,
       // packet number
@@ -13327,7 +13498,8 @@
 
   // Make sure we discard the subsequent zeroes.
   EXPECT_QUIC_PEER_BUG(EXPECT_TRUE(framer_.ProcessPacket(encrypted)),
-                       "Server: Received mismatched coalesced header.*");
+                       "Server: (Failed to parse received|Received mismatched) "
+                       "coalesced header.*");
 }
 
 TEST_P(QuicFramerTest, ClientReceivesInvalidVersion) {
@@ -13376,10 +13548,10 @@
   // clang-format off
   PacketFragments packet = {
       // type (8 byte connection_id and 1 byte packet number)
-      {"Unable to read type.",
+      {"Unable to read first byte.",
        {0x40}},
       // connection_id
-      {"Unable to read Destination ConnectionId.",
+      {"Unable to read destination connection ID.",
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x42}},
       // packet number
       {"Unable to read packet number.",
@@ -13388,10 +13560,10 @@
 
   PacketFragments packet_with_padding = {
       // type (8 byte connection_id and 1 byte packet number)
-      {"Unable to read type.",
+      {"Unable to read first byte.",
        {0x40}},
       // connection_id
-      {"Unable to read Destination ConnectionId.",
+      {"Unable to read destination connection ID.",
        {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x42}},
       // packet number
       {"",
@@ -13451,9 +13623,11 @@
        // version
        QUIC_VERSION_BYTES,
        // destination connection ID length
-       0x50,
+       0x08,
        // destination connection ID
        0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+       // source connection ID length
+       0x00,
        // long header packet length
        0x05,
        // packet number
@@ -13535,7 +13709,7 @@
   // clang-format off
   PacketFragments packet = {
     // public flags (IETF Retry packet, 0-length original destination CID)
-    {"Unable to read type.",
+    {"Unable to read first byte.",
      {0xf0}},
     // version tag
     {"Unable to read protocol version.",
@@ -13549,7 +13723,7 @@
   // clang-format off
   PacketFragments packet45 = {
     // public flags (IETF Retry packet, 0-length original destination CID)
-    {"Unable to read type.",
+    {"Unable to read first byte.",
      {0xf0}},
     // version tag
     {"Unable to read protocol version.",
@@ -13580,7 +13754,7 @@
   // clang-format off
   PacketFragments packet = {
     // public flags (IETF Retry packet, 0-length original destination CID)
-    {"Unable to read type.",
+    {"Unable to read first byte.",
      {0xf0}},
     // version tag
     {"Unable to read protocol version.",
@@ -13677,7 +13851,8 @@
   CheckFramingBoundaries(packet, QUIC_INVALID_PACKET_HEADER);
 }
 
-TEST_P(QuicFramerTest, WriteClientVersionNegotiationProbePacket) {
+TEST_P(QuicFramerTest, WriteClientVersionNegotiationProbePacketOld) {
+  SetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids, false);
   // clang-format off
   static const char expected_packet[1200] = {
     // IETF long header with fixed bit set, type initial, all-0 encrypted bits.
@@ -13726,17 +13901,18 @@
   EXPECT_TRUE(QuicFramer::WriteClientVersionNegotiationProbePacket(
       packet, sizeof(packet), destination_connection_id_bytes,
       sizeof(destination_connection_id_bytes)));
-  test::CompareCharArraysWithHexError("constructed packet", expected_packet,
-                                      sizeof(expected_packet), packet,
-                                      sizeof(packet));
+  test::CompareCharArraysWithHexError("constructed packet", packet,
+                                      sizeof(packet), expected_packet,
+                                      sizeof(expected_packet));
   QuicEncryptedPacket encrypted(reinterpret_cast<const char*>(packet),
                                 sizeof(packet), false);
   // Make sure we fail to parse this packet for the version under test.
-  EXPECT_FALSE(framer_.ProcessPacket(encrypted));
   if (framer_.transport_version() <= QUIC_VERSION_43) {
     // We can only parse the connection ID with an IETF parser.
+    EXPECT_FALSE(framer_.ProcessPacket(encrypted));
     return;
   }
+  EXPECT_TRUE(framer_.ProcessPacket(encrypted));
   ASSERT_TRUE(visitor_.header_.get());
   QuicConnectionId probe_payload_connection_id(
       reinterpret_cast<const char*>(destination_connection_id_bytes),
@@ -13745,7 +13921,232 @@
             visitor_.header_.get()->destination_connection_id);
 }
 
-TEST_P(QuicFramerTest, ParseServerVersionNegotiationProbeResponse) {
+TEST_P(QuicFramerTest, WriteClientVersionNegotiationProbePacket) {
+  SetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids, true);
+  // clang-format off
+  static const char expected_packet[1200] = {
+    // IETF long header with fixed bit set, type initial, all-0 encrypted bits.
+    0xc0,
+    // Version, part of the IETF space reserved for negotiation.
+    0xca, 0xba, 0xda, 0xda,
+    // Destination connection ID length 8.
+    0x08,
+    // 8-byte destination connection ID.
+    0x56, 0x4e, 0x20, 0x70, 0x6c, 0x7a, 0x20, 0x21,
+    // Source connection ID length 0.
+    0x00,
+    // 8 bytes of zeroes followed by 8 bytes of ones to ensure that this does
+    // not parse with any known version.
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    // zeroes to pad to 16 byte boundary.
+    0x00,
+    // A polite greeting in case a human sees this in tcpdump.
+    0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x61, 0x63,
+    0x6b, 0x65, 0x74, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
+    0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x20,
+    0x74, 0x6f, 0x20, 0x74, 0x72, 0x69, 0x67, 0x67,
+    0x65, 0x72, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20,
+    0x51, 0x55, 0x49, 0x43, 0x20, 0x76, 0x65, 0x72,
+    0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x65, 0x67,
+    0x6f, 0x74, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+    0x2e, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65,
+    0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64,
+    0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20,
+    0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20,
+    0x4e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74,
+    0x69, 0x6f, 0x6e, 0x20, 0x70, 0x61, 0x63, 0x6b,
+    0x65, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63,
+    0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x77, 0x68,
+    0x61, 0x74, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69,
+    0x6f, 0x6e, 0x73, 0x20, 0x79, 0x6f, 0x75, 0x20,
+    0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x2e,
+    0x20, 0x54, 0x68, 0x61, 0x6e, 0x6b, 0x20, 0x79,
+    0x6f, 0x75, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68,
+    0x61, 0x76, 0x65, 0x20, 0x61, 0x20, 0x6e, 0x69,
+    0x63, 0x65, 0x20, 0x64, 0x61, 0x79, 0x2e, 0x00,
+  };
+  // clang-format on
+  char packet[1200];
+  char destination_connection_id_bytes[] = {0x56, 0x4e, 0x20, 0x70,
+                                            0x6c, 0x7a, 0x20, 0x21};
+  EXPECT_TRUE(QuicFramer::WriteClientVersionNegotiationProbePacket(
+      packet, sizeof(packet), destination_connection_id_bytes,
+      sizeof(destination_connection_id_bytes)));
+  test::CompareCharArraysWithHexError("constructed packet", packet,
+                                      sizeof(packet), expected_packet,
+                                      sizeof(expected_packet));
+  QuicEncryptedPacket encrypted(reinterpret_cast<const char*>(packet),
+                                sizeof(packet), false);
+  if (framer_.transport_version() < QUIC_VERSION_99) {
+    // We can only parse the connection ID with a v99 parser.
+    EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+    return;
+  }
+  EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+  ASSERT_TRUE(visitor_.header_.get());
+  QuicConnectionId probe_payload_connection_id(
+      reinterpret_cast<const char*>(destination_connection_id_bytes),
+      sizeof(destination_connection_id_bytes));
+  EXPECT_EQ(probe_payload_connection_id,
+            visitor_.header_.get()->destination_connection_id);
+}
+
+TEST_P(QuicFramerTest, DispatcherParseOldClientVersionNegotiationProbePacket) {
+  // clang-format off
+  static const char packet[1200] = {
+    // IETF long header with fixed bit set, type initial, all-0 encrypted bits.
+    0xc0,
+    // Version, part of the IETF space reserved for negotiation.
+    0xca, 0xba, 0xda, 0xba,
+    // Destination connection ID length 8, source connection ID length 0.
+    0x50,
+    // 8-byte destination connection ID.
+    0x56, 0x4e, 0x20, 0x70, 0x6c, 0x7a, 0x20, 0x21,
+    // 8 bytes of zeroes followed by 8 bytes of ones to ensure that this does
+    // not parse with any known version.
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    // 2 bytes of zeroes to pad to 16 byte boundary.
+    0x00, 0x00,
+    // A polite greeting in case a human sees this in tcpdump.
+    0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x61, 0x63,
+    0x6b, 0x65, 0x74, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
+    0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x20,
+    0x74, 0x6f, 0x20, 0x74, 0x72, 0x69, 0x67, 0x67,
+    0x65, 0x72, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20,
+    0x51, 0x55, 0x49, 0x43, 0x20, 0x76, 0x65, 0x72,
+    0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x65, 0x67,
+    0x6f, 0x74, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+    0x2e, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65,
+    0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64,
+    0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20,
+    0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20,
+    0x4e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74,
+    0x69, 0x6f, 0x6e, 0x20, 0x70, 0x61, 0x63, 0x6b,
+    0x65, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63,
+    0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x77, 0x68,
+    0x61, 0x74, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69,
+    0x6f, 0x6e, 0x73, 0x20, 0x79, 0x6f, 0x75, 0x20,
+    0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x2e,
+    0x20, 0x54, 0x68, 0x61, 0x6e, 0x6b, 0x20, 0x79,
+    0x6f, 0x75, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68,
+    0x61, 0x76, 0x65, 0x20, 0x61, 0x20, 0x6e, 0x69,
+    0x63, 0x65, 0x20, 0x64, 0x61, 0x79, 0x2e, 0x00,
+  };
+  // clang-format on
+  char expected_destination_connection_id_bytes[] = {0x56, 0x4e, 0x20, 0x70,
+                                                     0x6c, 0x7a, 0x20, 0x21};
+  QuicConnectionId expected_destination_connection_id(
+      reinterpret_cast<const char*>(expected_destination_connection_id_bytes),
+      sizeof(expected_destination_connection_id_bytes));
+
+  QuicEncryptedPacket encrypted(reinterpret_cast<const char*>(packet),
+                                sizeof(packet));
+  PacketHeaderFormat format = GOOGLE_QUIC_PACKET;
+  bool version_present = false, has_length_prefix = true;
+  QuicVersionLabel version_label = 33;
+  ParsedQuicVersion parsed_version = UnsupportedQuicVersion();
+  QuicConnectionId destination_connection_id = TestConnectionId(1);
+  QuicConnectionId source_connection_id = TestConnectionId(2);
+  bool retry_token_present = true;
+  QuicStringPiece retry_token;
+  std::string detailed_error = "foobar";
+  QuicErrorCode header_parse_result = QuicFramer::ParsePublicHeaderDispatcher(
+      encrypted, kQuicDefaultConnectionIdLength, &format, &version_present,
+      &has_length_prefix, &version_label, &parsed_version,
+      &destination_connection_id, &source_connection_id, &retry_token_present,
+      &retry_token, &detailed_error);
+  EXPECT_EQ(QUIC_NO_ERROR, header_parse_result);
+  EXPECT_EQ(IETF_QUIC_LONG_HEADER_PACKET, format);
+  EXPECT_TRUE(version_present);
+  EXPECT_FALSE(has_length_prefix);
+  EXPECT_EQ(0xcabadaba, version_label);
+  EXPECT_EQ(expected_destination_connection_id, destination_connection_id);
+  EXPECT_EQ(EmptyQuicConnectionId(), source_connection_id);
+  EXPECT_FALSE(retry_token_present);
+  EXPECT_EQ("", detailed_error);
+}
+
+TEST_P(QuicFramerTest, DispatcherParseClientVersionNegotiationProbePacket) {
+  // clang-format off
+  static const char packet[1200] = {
+    // IETF long header with fixed bit set, type initial, all-0 encrypted bits.
+    0xc0,
+    // Version, part of the IETF space reserved for negotiation.
+    0xca, 0xba, 0xda, 0xba,
+    // Destination connection ID length 8.
+    0x08,
+    // 8-byte destination connection ID.
+    0x56, 0x4e, 0x20, 0x70, 0x6c, 0x7a, 0x20, 0x21,
+    // Source connection ID length 0.
+    0x00,
+    // 8 bytes of zeroes followed by 8 bytes of ones to ensure that this does
+    // not parse with any known version.
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    // 1 byte of zeroes to pad to 16 byte boundary.
+    0x00,
+    // A polite greeting in case a human sees this in tcpdump.
+    0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x61, 0x63,
+    0x6b, 0x65, 0x74, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
+    0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x20,
+    0x74, 0x6f, 0x20, 0x74, 0x72, 0x69, 0x67, 0x67,
+    0x65, 0x72, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20,
+    0x51, 0x55, 0x49, 0x43, 0x20, 0x76, 0x65, 0x72,
+    0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x65, 0x67,
+    0x6f, 0x74, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+    0x2e, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65,
+    0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64,
+    0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20,
+    0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20,
+    0x4e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74,
+    0x69, 0x6f, 0x6e, 0x20, 0x70, 0x61, 0x63, 0x6b,
+    0x65, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63,
+    0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x77, 0x68,
+    0x61, 0x74, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69,
+    0x6f, 0x6e, 0x73, 0x20, 0x79, 0x6f, 0x75, 0x20,
+    0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x2e,
+    0x20, 0x54, 0x68, 0x61, 0x6e, 0x6b, 0x20, 0x79,
+    0x6f, 0x75, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68,
+    0x61, 0x76, 0x65, 0x20, 0x61, 0x20, 0x6e, 0x69,
+    0x63, 0x65, 0x20, 0x64, 0x61, 0x79, 0x2e, 0x00,
+  };
+  // clang-format on
+  char expected_destination_connection_id_bytes[] = {0x56, 0x4e, 0x20, 0x70,
+                                                     0x6c, 0x7a, 0x20, 0x21};
+  QuicConnectionId expected_destination_connection_id(
+      reinterpret_cast<const char*>(expected_destination_connection_id_bytes),
+      sizeof(expected_destination_connection_id_bytes));
+
+  QuicEncryptedPacket encrypted(reinterpret_cast<const char*>(packet),
+                                sizeof(packet));
+  PacketHeaderFormat format = GOOGLE_QUIC_PACKET;
+  bool version_present = false, has_length_prefix = false;
+  QuicVersionLabel version_label = 33;
+  ParsedQuicVersion parsed_version = UnsupportedQuicVersion();
+  QuicConnectionId destination_connection_id = TestConnectionId(1);
+  QuicConnectionId source_connection_id = TestConnectionId(2);
+  bool retry_token_present = true;
+  QuicStringPiece retry_token;
+  std::string detailed_error = "foobar";
+  QuicErrorCode header_parse_result = QuicFramer::ParsePublicHeaderDispatcher(
+      encrypted, kQuicDefaultConnectionIdLength, &format, &version_present,
+      &has_length_prefix, &version_label, &parsed_version,
+      &destination_connection_id, &source_connection_id, &retry_token_present,
+      &retry_token, &detailed_error);
+  EXPECT_EQ(QUIC_NO_ERROR, header_parse_result);
+  EXPECT_EQ(IETF_QUIC_LONG_HEADER_PACKET, format);
+  EXPECT_TRUE(version_present);
+  EXPECT_TRUE(has_length_prefix);
+  EXPECT_EQ(0xcabadaba, version_label);
+  EXPECT_EQ(expected_destination_connection_id, destination_connection_id);
+  EXPECT_EQ(EmptyQuicConnectionId(), source_connection_id);
+  EXPECT_EQ("", detailed_error);
+}
+
+TEST_P(QuicFramerTest, ParseServerVersionNegotiationProbeResponseOld) {
+  SetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids, false);
   // clang-format off
   const char packet[] = {
     // IETF long header with fixed bit set, type initial, all-0 encrypted bits.
@@ -13771,8 +14172,39 @@
       &parsed_probe_payload_length, &parse_detailed_error));
   EXPECT_EQ("", parse_detailed_error);
   test::CompareCharArraysWithHexError(
-      "parsed probe", probe_payload_bytes, sizeof(probe_payload_bytes),
-      parsed_probe_payload_bytes, parsed_probe_payload_length);
+      "parsed probe", parsed_probe_payload_bytes, parsed_probe_payload_length,
+      probe_payload_bytes, sizeof(probe_payload_bytes));
+}
+
+TEST_P(QuicFramerTest, ParseServerVersionNegotiationProbeResponse) {
+  SetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids, true);
+  // clang-format off
+  const char packet[] = {
+    // IETF long header with fixed bit set, type initial, all-0 encrypted bits.
+    0xc0,
+    // Version of 0, indicating version negotiation.
+    0x00, 0x00, 0x00, 0x00,
+    // Destination connection ID length 0, source connection ID length 8.
+    0x00, 0x08,
+    // 8-byte source connection ID.
+    0x56, 0x4e, 0x20, 0x70, 0x6c, 0x7a, 0x20, 0x21,
+    // A few supported versions.
+    0xaa, 0xaa, 0xaa, 0xaa,
+    QUIC_VERSION_BYTES,
+  };
+  // clang-format on
+  char probe_payload_bytes[] = {0x56, 0x4e, 0x20, 0x70, 0x6c, 0x7a, 0x20, 0x21};
+  char parsed_probe_payload_bytes[kQuicMaxConnectionIdLength] = {};
+  uint8_t parsed_probe_payload_length = 0;
+  std::string parse_detailed_error = "";
+  EXPECT_TRUE(QuicFramer::ParseServerVersionNegotiationProbeResponse(
+      reinterpret_cast<const char*>(packet), sizeof(packet),
+      reinterpret_cast<char*>(parsed_probe_payload_bytes),
+      &parsed_probe_payload_length, &parse_detailed_error));
+  EXPECT_EQ("", parse_detailed_error);
+  test::CompareCharArraysWithHexError(
+      "parsed probe", parsed_probe_payload_bytes, parsed_probe_payload_length,
+      probe_payload_bytes, sizeof(probe_payload_bytes));
 }
 
 TEST_P(QuicFramerTest, ClientConnectionIdFromLongHeaderToClient) {
@@ -13802,9 +14234,34 @@
     // padding frame
     0x00,
   };
+  unsigned char packet99[] = {
+    // public flags (long header with packet type HANDSHAKE and
+    // 4-byte packet number)
+    0xE3,
+    // version
+    QUIC_VERSION_BYTES,
+    // destination connection ID length
+    0x08,
+    // destination connection ID
+    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+    // source connection ID length
+    0x00,
+    // long header packet length
+    0x05,
+    // packet number
+    0x12, 0x34, 0x56, 0x00,
+    // padding frame
+    0x00,
+  };
   // clang-format on
-  const bool parse_success = framer_.ProcessPacket(
-      QuicEncryptedPacket(AsChars(packet), QUIC_ARRAYSIZE(packet), false));
+  unsigned char* p = packet;
+  size_t p_length = QUIC_ARRAYSIZE(packet);
+  if (framer_.transport_version() == QUIC_VERSION_99) {
+    p = packet99;
+    p_length = QUIC_ARRAYSIZE(packet99);
+  }
+  const bool parse_success =
+      framer_.ProcessPacket(QuicEncryptedPacket(AsChars(p), p_length, false));
   if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
           framer_.transport_version())) {
     EXPECT_FALSE(parse_success);
@@ -13855,9 +14312,32 @@
     // padding frame
     0x00,
   };
+  unsigned char packet99[] = {
+    // public flags (long header with packet type HANDSHAKE and
+    // 4-byte packet number)
+    0xE3,
+    // version
+    QUIC_VERSION_BYTES,
+    // connection ID lengths
+    0x00, 0x08,
+    // source connection ID
+    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+    // long header packet length
+    0x05,
+    // packet number
+    0x12, 0x34, 0x56, 0x00,
+    // padding frame
+    0x00,
+  };
   // clang-format on
-  const bool parse_success = framer_.ProcessPacket(
-      QuicEncryptedPacket(AsChars(packet), QUIC_ARRAYSIZE(packet), false));
+  unsigned char* p = packet;
+  size_t p_length = QUIC_ARRAYSIZE(packet);
+  if (framer_.transport_version() == QUIC_VERSION_99) {
+    p = packet99;
+    p_length = QUIC_ARRAYSIZE(packet99);
+  }
+  const bool parse_success =
+      framer_.ProcessPacket(QuicEncryptedPacket(AsChars(p), p_length, false));
   if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
           framer_.transport_version())) {
     EXPECT_FALSE(parse_success);
diff --git a/quic/core/quic_ietf_framer_test.cc b/quic/core/quic_ietf_framer_test.cc
index dbbacdd..f0872b6 100644
--- a/quic/core/quic_ietf_framer_test.cc
+++ b/quic/core/quic_ietf_framer_test.cc
@@ -102,7 +102,7 @@
 
   bool OnProtocolVersionMismatch(
       ParsedQuicVersion /*received_version*/) override {
-    return true;
+    return false;
   }
 
   bool OnUnauthenticatedPublicHeader(
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc
index a8d3291..27c913e 100644
--- a/quic/core/quic_packet_creator.cc
+++ b/quic/core/quic_packet_creator.cc
@@ -640,12 +640,13 @@
 std::unique_ptr<QuicEncryptedPacket>
 QuicPacketCreator::SerializeVersionNegotiationPacket(
     bool ietf_quic,
+    bool use_length_prefix,
     const ParsedQuicVersionVector& supported_versions) {
   DCHECK_EQ(Perspective::IS_SERVER, framer_->perspective());
   std::unique_ptr<QuicEncryptedPacket> encrypted =
-      QuicFramer::BuildVersionNegotiationPacket(server_connection_id_,
-                                                client_connection_id_,
-                                                ietf_quic, supported_versions);
+      QuicFramer::BuildVersionNegotiationPacket(
+          server_connection_id_, client_connection_id_, ietf_quic,
+          use_length_prefix, supported_versions);
   DCHECK(encrypted);
   DCHECK_GE(max_packet_length_, encrypted->length());
   return encrypted;
diff --git a/quic/core/quic_packet_creator.h b/quic/core/quic_packet_creator.h
index 2c2fffb..affc15c 100644
--- a/quic/core/quic_packet_creator.h
+++ b/quic/core/quic_packet_creator.h
@@ -187,6 +187,7 @@
   // Creates a version negotiation packet which supports |supported_versions|.
   std::unique_ptr<QuicEncryptedPacket> SerializeVersionNegotiationPacket(
       bool ietf_quic,
+      bool use_length_prefix,
       const ParsedQuicVersionVector& supported_versions);
 
   // Creates a connectivity probing packet for versions prior to version 99.
diff --git a/quic/core/quic_packet_creator_test.cc b/quic/core/quic_packet_creator_test.cc
index 802c07b..bd49182 100644
--- a/quic/core/quic_packet_creator_test.cc
+++ b/quic/core/quic_packet_creator_test.cc
@@ -813,8 +813,11 @@
   versions.push_back(test::QuicVersionMax());
   const bool ietf_quic =
       VersionHasIetfInvariantHeader(GetParam().version.transport_version);
+  const bool has_length_prefix =
+      GetParam().version.HasLengthPrefixedConnectionIds();
   std::unique_ptr<QuicEncryptedPacket> encrypted(
-      creator_.SerializeVersionNegotiationPacket(ietf_quic, versions));
+      creator_.SerializeVersionNegotiationPacket(ietf_quic, has_length_prefix,
+                                                 versions));
 
   {
     InSequence s;
@@ -1820,6 +1823,9 @@
   if (QuicVersionHasLongHeaderLengths(version)) {
     expected_largest_payload -= 2;
   }
+  if (GetParam().version.HasLengthPrefixedConnectionIds()) {
+    expected_largest_payload -= 1;
+  }
   EXPECT_EQ(expected_largest_payload,
             creator_.GetGuaranteedLargestMessagePayload());
 }
diff --git a/quic/core/quic_packet_generator.cc b/quic/core/quic_packet_generator.cc
index fe17069..160222f 100644
--- a/quic/core/quic_packet_generator.cc
+++ b/quic/core/quic_packet_generator.cc
@@ -297,9 +297,10 @@
 std::unique_ptr<QuicEncryptedPacket>
 QuicPacketGenerator::SerializeVersionNegotiationPacket(
     bool ietf_quic,
+    bool use_length_prefix,
     const ParsedQuicVersionVector& supported_versions) {
-  return packet_creator_.SerializeVersionNegotiationPacket(ietf_quic,
-                                                           supported_versions);
+  return packet_creator_.SerializeVersionNegotiationPacket(
+      ietf_quic, use_length_prefix, supported_versions);
 }
 
 OwningSerializedPacketPointer
diff --git a/quic/core/quic_packet_generator.h b/quic/core/quic_packet_generator.h
index ea7da5d..c6902e0 100644
--- a/quic/core/quic_packet_generator.h
+++ b/quic/core/quic_packet_generator.h
@@ -141,6 +141,7 @@
   // Creates a version negotiation packet which supports |supported_versions|.
   std::unique_ptr<QuicEncryptedPacket> SerializeVersionNegotiationPacket(
       bool ietf_quic,
+      bool use_length_prefix,
       const ParsedQuicVersionVector& supported_versions);
 
   // Creates a connectivity probing packet.
diff --git a/quic/core/quic_packets.cc b/quic/core/quic_packets.cc
index 4887e82..7d4769f 100644
--- a/quic/core/quic_packets.cc
+++ b/quic/core/quic_packets.cc
@@ -134,6 +134,9 @@
       if (include_diversification_nonce) {
         size += kDiversificationNonceSize;
       }
+      if (VersionHasLengthPrefixedConnectionIds(version)) {
+        size += kConnectionIdLengthSize;
+      }
       DCHECK(QuicVersionHasLongHeaderLengths(version) ||
              !GetQuicReloadableFlag(quic_fix_get_packet_header_size) ||
              retry_token_length_length + retry_token_length + length_length ==
@@ -508,6 +511,7 @@
       packet(packet),
       form(GOOGLE_QUIC_PACKET),
       version_flag(false),
+      use_length_prefix(false),
       version_label(0),
       version(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED),
       destination_connection_id(EmptyQuicConnectionId()),
diff --git a/quic/core/quic_packets.h b/quic/core/quic_packets.h
index cf6099e..3af15ea 100644
--- a/quic/core/quic_packets.h
+++ b/quic/core/quic_packets.h
@@ -425,6 +425,7 @@
   // Fields below are populated by QuicFramer::ProcessPacketDispatcher.
   PacketHeaderFormat form;
   bool version_flag;
+  bool use_length_prefix;
   QuicVersionLabel version_label;
   ParsedQuicVersion version;
   QuicConnectionId destination_connection_id;
diff --git a/quic/core/quic_time_wait_list_manager.cc b/quic/core/quic_time_wait_list_manager.cc
index cb29a40..a0e7cfb 100644
--- a/quic/core/quic_time_wait_list_manager.cc
+++ b/quic/core/quic_time_wait_list_manager.cc
@@ -206,17 +206,20 @@
     QuicConnectionId server_connection_id,
     QuicConnectionId client_connection_id,
     bool ietf_quic,
+    bool use_length_prefix,
     const ParsedQuicVersionVector& supported_versions,
     const QuicSocketAddress& self_address,
     const QuicSocketAddress& peer_address,
     std::unique_ptr<QuicPerPacketContext> packet_context) {
   std::unique_ptr<QuicEncryptedPacket> version_packet =
-      QuicFramer::BuildVersionNegotiationPacket(server_connection_id,
-                                                client_connection_id, ietf_quic,
-                                                supported_versions);
+      QuicFramer::BuildVersionNegotiationPacket(
+          server_connection_id, client_connection_id, ietf_quic,
+          use_length_prefix, supported_versions);
   QUIC_DVLOG(2) << "Dispatcher sending version negotiation packet: {"
                 << ParsedQuicVersionVectorToString(supported_versions) << "}, "
-                << (ietf_quic ? "" : "!") << "ietf_quic:" << std::endl
+                << (ietf_quic ? "" : "!")
+                << "ietf_quic:" << (use_length_prefix ? "" : "!")
+                << "use_length_prefix:" << std::endl
                 << QuicTextUtils::HexDump(QuicStringPiece(
                        version_packet->data(), version_packet->length()));
   SendOrQueuePacket(QuicMakeUnique<QueuedPacket>(self_address, peer_address,
diff --git a/quic/core/quic_time_wait_list_manager.h b/quic/core/quic_time_wait_list_manager.h
index c53632c..9a807c0 100644
--- a/quic/core/quic_time_wait_list_manager.h
+++ b/quic/core/quic_time_wait_list_manager.h
@@ -125,6 +125,7 @@
       QuicConnectionId server_connection_id,
       QuicConnectionId client_connection_id,
       bool ietf_quic,
+      bool use_length_prefix,
       const ParsedQuicVersionVector& supported_versions,
       const QuicSocketAddress& self_address,
       const QuicSocketAddress& peer_address,
diff --git a/quic/core/quic_time_wait_list_manager_test.cc b/quic/core/quic_time_wait_list_manager_test.cc
index 0d9e32a..daa6431 100644
--- a/quic/core/quic_time_wait_list_manager_test.cc
+++ b/quic/core/quic_time_wait_list_manager_test.cc
@@ -251,31 +251,50 @@
 
 TEST_F(QuicTimeWaitListManagerTest, SendVersionNegotiationPacket) {
   std::unique_ptr<QuicEncryptedPacket> packet(
-      QuicFramer::BuildVersionNegotiationPacket(connection_id_,
-                                                EmptyQuicConnectionId(), false,
-                                                AllSupportedVersions()));
+      QuicFramer::BuildVersionNegotiationPacket(
+          connection_id_, EmptyQuicConnectionId(), /*ietf_quic=*/false,
+          /*use_length_prefix=*/false, AllSupportedVersions()));
   EXPECT_CALL(writer_, WritePacket(_, packet->length(), self_address_.host(),
                                    peer_address_, _))
       .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1)));
 
   time_wait_list_manager_.SendVersionNegotiationPacket(
-      connection_id_, EmptyQuicConnectionId(), false, AllSupportedVersions(),
-      self_address_, peer_address_, QuicMakeUnique<QuicPerPacketContext>());
+      connection_id_, EmptyQuicConnectionId(), /*ietf_quic=*/false,
+      /*use_length_prefix=*/false, AllSupportedVersions(), self_address_,
+      peer_address_, QuicMakeUnique<QuicPerPacketContext>());
+  EXPECT_EQ(0u, time_wait_list_manager_.num_connections());
+}
+
+TEST_F(QuicTimeWaitListManagerTest,
+       SendIetfVersionNegotiationPacketWithoutLengthPrefix) {
+  std::unique_ptr<QuicEncryptedPacket> packet(
+      QuicFramer::BuildVersionNegotiationPacket(
+          connection_id_, EmptyQuicConnectionId(), /*ietf_quic=*/true,
+          /*use_length_prefix=*/false, AllSupportedVersions()));
+  EXPECT_CALL(writer_, WritePacket(_, packet->length(), self_address_.host(),
+                                   peer_address_, _))
+      .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1)));
+
+  time_wait_list_manager_.SendVersionNegotiationPacket(
+      connection_id_, EmptyQuicConnectionId(), /*ietf_quic=*/true,
+      /*use_length_prefix=*/false, AllSupportedVersions(), self_address_,
+      peer_address_, QuicMakeUnique<QuicPerPacketContext>());
   EXPECT_EQ(0u, time_wait_list_manager_.num_connections());
 }
 
 TEST_F(QuicTimeWaitListManagerTest, SendIetfVersionNegotiationPacket) {
   std::unique_ptr<QuicEncryptedPacket> packet(
-      QuicFramer::BuildVersionNegotiationPacket(connection_id_,
-                                                EmptyQuicConnectionId(), true,
-                                                AllSupportedVersions()));
+      QuicFramer::BuildVersionNegotiationPacket(
+          connection_id_, EmptyQuicConnectionId(), /*ietf_quic=*/true,
+          /*use_length_prefix=*/true, AllSupportedVersions()));
   EXPECT_CALL(writer_, WritePacket(_, packet->length(), self_address_.host(),
                                    peer_address_, _))
       .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1)));
 
   time_wait_list_manager_.SendVersionNegotiationPacket(
-      connection_id_, EmptyQuicConnectionId(), true, AllSupportedVersions(),
-      self_address_, peer_address_, QuicMakeUnique<QuicPerPacketContext>());
+      connection_id_, EmptyQuicConnectionId(), /*ietf_quic=*/true,
+      /*use_length_prefix=*/true, AllSupportedVersions(), self_address_,
+      peer_address_, QuicMakeUnique<QuicPerPacketContext>());
   EXPECT_EQ(0u, time_wait_list_manager_.num_connections());
 }
 
@@ -285,16 +304,17 @@
   SetQuicRestartFlag(quic_do_not_override_connection_id, true);
 
   std::unique_ptr<QuicEncryptedPacket> packet(
-      QuicFramer::BuildVersionNegotiationPacket(connection_id_,
-                                                TestConnectionId(0x33), true,
-                                                AllSupportedVersions()));
+      QuicFramer::BuildVersionNegotiationPacket(
+          connection_id_, TestConnectionId(0x33), /*ietf_quic=*/true,
+          /*use_length_prefix=*/true, AllSupportedVersions()));
   EXPECT_CALL(writer_, WritePacket(_, packet->length(), self_address_.host(),
                                    peer_address_, _))
       .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1)));
 
   time_wait_list_manager_.SendVersionNegotiationPacket(
-      connection_id_, TestConnectionId(0x33), true, AllSupportedVersions(),
-      self_address_, peer_address_, QuicMakeUnique<QuicPerPacketContext>());
+      connection_id_, TestConnectionId(0x33), /*ietf_quic=*/true,
+      /*use_length_prefix=*/true, AllSupportedVersions(), self_address_,
+      peer_address_, QuicMakeUnique<QuicPerPacketContext>());
   EXPECT_EQ(0u, time_wait_list_manager_.num_connections());
 }
 
diff --git a/quic/core/quic_utils.h b/quic/core/quic_utils.h
index 294f52f..db29740 100644
--- a/quic/core/quic_utils.h
+++ b/quic/core/quic_utils.h
@@ -109,7 +109,7 @@
       TransmissionType retransmission_type);
 
   // Returns true if header with |first_byte| is considered as an IETF QUIC
-  // packet header.
+  // packet header. This only works on the server.
   static bool IsIetfPacketHeader(uint8_t first_byte);
 
   // Returns true if header with |first_byte| is considered as an IETF QUIC
diff --git a/quic/core/quic_versions.cc b/quic/core/quic_versions.cc
index 950dd1d..406d44b 100644
--- a/quic/core/quic_versions.cc
+++ b/quic/core/quic_versions.cc
@@ -84,6 +84,10 @@
   return VersionLacksHeadersStream(transport_version);
 }
 
+bool ParsedQuicVersion::HasLengthPrefixedConnectionIds() const {
+  return VersionHasLengthPrefixedConnectionIds(transport_version);
+}
+
 bool VersionLacksHeadersStream(QuicTransportVersion transport_version) {
   if (GetQuicFlag(FLAGS_quic_headers_stream_id_in_v99) == 0) {
     return false;
@@ -91,6 +95,11 @@
   return transport_version == QUIC_VERSION_99;
 }
 
+bool VersionHasLengthPrefixedConnectionIds(
+    QuicTransportVersion transport_version) {
+  return transport_version >= QUIC_VERSION_99;
+}
+
 std::ostream& operator<<(std::ostream& os, const ParsedQuicVersion& version) {
   os << ParsedQuicVersionToString(version);
   return os;
@@ -418,6 +427,34 @@
   return result;
 }
 
+bool QuicVersionLabelUses4BitConnectionIdLength(
+    QuicVersionLabel version_label) {
+  // As we deprecate old versions, we still need the ability to send valid
+  // version negotiation packets for those versions. This function keeps track
+  // of the versions that ever supported the 4bit connection ID length encoding
+  // that we know about. Google QUIC 43 and earlier used a different encoding,
+  // and Google QUIC 49 will start using the new length prefixed encoding.
+  // Similarly, only IETF drafts 11 to 21 used this encoding.
+
+  // Check Q044, Q045, Q046, Q047 and Q048.
+  for (uint8_t c = '4'; c <= '8'; ++c) {
+    if (version_label == MakeVersionLabel('Q', '0', '4', c)) {
+      return true;
+    }
+  }
+  // Check T048.
+  if (version_label == MakeVersionLabel('T', '0', '4', '8')) {
+    return true;
+  }
+  // Check IETF draft versions in [11,21].
+  for (uint8_t draft_number = 11; draft_number <= 21; ++draft_number) {
+    if (version_label == MakeVersionLabel(0xff, 0x00, 0x00, draft_number)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 ParsedQuicVersion UnsupportedQuicVersion() {
   return ParsedQuicVersion(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED);
 }
@@ -453,6 +490,7 @@
   SetQuicFlag(FLAGS_quic_supports_tls_handshake, true);
   SetQuicFlag(FLAGS_quic_headers_stream_id_in_v99, 60);
   SetQuicReloadableFlag(quic_simplify_stop_waiting, true);
+  SetQuicReloadableFlag(quic_use_parse_public_header, true);
   SetQuicRestartFlag(quic_do_not_override_connection_id, true);
   SetQuicRestartFlag(quic_use_allocated_connection_ids, true);
   SetQuicRestartFlag(quic_dispatcher_hands_chlo_extractor_one_version, true);
diff --git a/quic/core/quic_versions.h b/quic/core/quic_versions.h
index 72bb32c..3edb895 100644
--- a/quic/core/quic_versions.h
+++ b/quic/core/quic_versions.h
@@ -175,6 +175,11 @@
 
   // Returns whether this version does not have the Google QUIC headers stream.
   bool DoesNotHaveHeadersStream() const;
+
+  // Returns whether this version supports long header 8-bit encoded
+  // connection ID lengths as described in draft-ietf-quic-invariants-06 and
+  // draft-ietf-quic-transport-22.
+  bool HasLengthPrefixedConnectionIds() const;
 };
 
 QUIC_EXPORT_PRIVATE ParsedQuicVersion UnsupportedQuicVersion();
@@ -426,6 +431,18 @@
   return transport_version >= QUIC_VERSION_99;
 }
 
+// Returns whether this version supports long header 8-bit encoded
+// connection ID lengths as described in draft-ietf-quic-invariants-06 and
+// draft-ietf-quic-transport-22.
+QUIC_EXPORT_PRIVATE bool VersionHasLengthPrefixedConnectionIds(
+    QuicTransportVersion transport_version);
+
+// Returns whether this version label supports long header 4-bit encoded
+// connection ID lengths as described in draft-ietf-quic-invariants-05 and
+// draft-ietf-quic-transport-21.
+QUIC_EXPORT_PRIVATE bool QuicVersionLabelUses4BitConnectionIdLength(
+    QuicVersionLabel version_label);
+
 // Returns the ALPN string to use in TLS for this version of QUIC.
 QUIC_EXPORT_PRIVATE std::string AlpnForVersion(
     ParsedQuicVersion parsed_version);
diff --git a/quic/test_tools/mock_quic_time_wait_list_manager.h b/quic/test_tools/mock_quic_time_wait_list_manager.h
index b46e68b..6b2b6d0 100644
--- a/quic/test_tools/mock_quic_time_wait_list_manager.h
+++ b/quic/test_tools/mock_quic_time_wait_list_manager.h
@@ -45,10 +45,11 @@
                     PacketHeaderFormat header_format,
                     std::unique_ptr<QuicPerPacketContext> packet_context));
 
-  MOCK_METHOD7(SendVersionNegotiationPacket,
+  MOCK_METHOD8(SendVersionNegotiationPacket,
                void(QuicConnectionId server_connection_id,
                     QuicConnectionId client_connection_id,
                     bool ietf_quic,
+                    bool has_length_prefix,
                     const ParsedQuicVersionVector& supported_versions,
                     const QuicSocketAddress& server_address,
                     const QuicSocketAddress& client_address,