WIP RFCv1 test
diff --git a/quic/core/crypto/crypto_utils.cc b/quic/core/crypto/crypto_utils.cc
index f8eb06a..cb106ac 100644
--- a/quic/core/crypto/crypto_utils.cc
+++ b/quic/core/crypto/crypto_utils.cc
@@ -135,6 +135,9 @@
 const uint8_t kDraft29InitialSalt[] = {0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2,
                                        0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61,
                                        0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99};
+const uint8_t kRFCv1InitialSalt[] = {0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34,
+                                     0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8,
+                                     0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a};
 
 // Salts used by deployed versions of QUIC. When introducing a new version,
 // generate a new salt by running `openssl rand -hex 20`.
@@ -155,9 +158,12 @@
 
 const uint8_t* InitialSaltForVersion(const ParsedQuicVersion& version,
                                      size_t* out_len) {
-  static_assert(SupportedVersions().size() == 5u,
+  static_assert(SupportedVersions().size() == 6u,
                 "Supported versions out of sync with initial encryption salts");
-  if (version == ParsedQuicVersion::Draft29()) {
+  if (version == ParsedQuicVersion::RFCv1()) {
+    *out_len = ABSL_ARRAYSIZE(kRFCv1InitialSalt);
+    return kRFCv1InitialSalt;
+  } else if (version == ParsedQuicVersion::Draft29()) {
     *out_len = ABSL_ARRAYSIZE(kDraft29InitialSalt);
     return kDraft29InitialSalt;
   } else if (version == ParsedQuicVersion::T051()) {
@@ -184,6 +190,11 @@
                                              0x6c, 0xb9, 0x6b, 0xe1};
 const uint8_t kDraft29RetryIntegrityNonce[] = {
     0xe5, 0x49, 0x30, 0xf9, 0x7f, 0x21, 0x36, 0xf0, 0x53, 0x0a, 0x8c, 0x1c};
+const uint8_t kRFCv1RetryIntegrityKey[] = {0xbe, 0x0c, 0x69, 0x0b, 0x9f, 0x66,
+                                           0x57, 0x5a, 0x1d, 0x76, 0x6b, 0x54,
+                                           0xe3, 0x68, 0xc8, 0x4e};
+const uint8_t kRFCv1RetryIntegrityNonce[] = {
+    0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, 0x25, 0xbb};
 
 // Keys used by Google versions of QUIC. When introducing a new version,
 // generate a new key by running `openssl rand -hex 16`.
@@ -205,12 +216,20 @@
 bool RetryIntegrityKeysForVersion(const ParsedQuicVersion& version,
                                   absl::string_view* key,
                                   absl::string_view* nonce) {
-  static_assert(SupportedVersions().size() == 5u,
+  static_assert(SupportedVersions().size() == 6u,
                 "Supported versions out of sync with retry integrity keys");
   if (!version.UsesTls()) {
     QUIC_BUG << "Attempted to get retry integrity keys for invalid version "
              << version;
     return false;
+  } else if (version == ParsedQuicVersion::RFCv1()) {
+    *key = absl::string_view(
+        reinterpret_cast<const char*>(kRFCv1RetryIntegrityKey),
+        ABSL_ARRAYSIZE(kRFCv1RetryIntegrityKey));
+    *nonce = absl::string_view(
+        reinterpret_cast<const char*>(kRFCv1RetryIntegrityNonce),
+        ABSL_ARRAYSIZE(kRFCv1RetryIntegrityNonce));
+    return true;
   } else if (version == ParsedQuicVersion::Draft29()) {
     *key = absl::string_view(
         reinterpret_cast<const char*>(kDraft29RetryIntegrityKey),
diff --git a/quic/core/http/http_decoder.cc b/quic/core/http/http_decoder.cc
index 590f2fc..dc67302 100644
--- a/quic/core/http/http_decoder.cc
+++ b/quic/core/http/http_decoder.cc
@@ -238,9 +238,20 @@
         QUIC_CODE_COUNT_N(quic_new_priority_update_frame, 2, 2);
         continue_processing =
             visitor_->OnPriorityUpdateFrameStart(header_length);
-        break;
+      } else {
+        continue_processing = visitor_->OnUnknownFrameStart(
+            current_frame_type_, header_length, current_frame_length_);
       }
-      ABSL_FALLTHROUGH_INTENDED;
+      break;
+    case static_cast<uint64_t>(HttpFrameType::ACCEPT_CH):
+      if (GetQuicReloadableFlag(quic_parse_accept_ch_frame)) {
+        QUIC_RELOADABLE_FLAG_COUNT(quic_parse_accept_ch_frame);
+        continue_processing = visitor_->OnAcceptChFrameStart(header_length);
+      } else {
+        continue_processing = visitor_->OnUnknownFrameStart(
+            current_frame_type_, header_length, current_frame_length_);
+      }
+      break;
     default:
       continue_processing = visitor_->OnUnknownFrameStart(
           current_frame_type_, header_length, current_frame_length_);
@@ -374,19 +385,23 @@
         // TODO(bnc): Avoid buffering if the entire frame is present, and
         // instead parse directly out of |reader|.
         BufferFramePayload(reader);
-        break;
+      } else {
+        continue_processing = HandleUnknownFramePayload(reader);
       }
-      ABSL_FALLTHROUGH_INTENDED;
+      break;
+    }
+    case static_cast<uint64_t>(HttpFrameType::ACCEPT_CH): {
+      if (GetQuicReloadableFlag(quic_parse_accept_ch_frame)) {
+        // TODO(bnc): Avoid buffering if the entire frame is present, and
+        // instead parse directly out of |reader|.
+        BufferFramePayload(reader);
+      } else {
+        continue_processing = HandleUnknownFramePayload(reader);
+      }
+      break;
     }
     default: {
-      QuicByteCount bytes_to_read = std::min<QuicByteCount>(
-          remaining_frame_length_, reader->BytesRemaining());
-      absl::string_view payload;
-      bool success = reader->ReadStringPiece(&payload, bytes_to_read);
-      DCHECK(success);
-      DCHECK(!payload.empty());
-      continue_processing = visitor_->OnUnknownFramePayload(payload);
-      remaining_frame_length_ -= payload.length();
+      continue_processing = HandleUnknownFramePayload(reader);
       break;
     }
   }
@@ -492,14 +507,28 @@
           return false;
         }
         continue_processing = visitor_->OnPriorityUpdateFrame(frame);
-        break;
+      } else {
+        continue_processing = visitor_->OnUnknownFrameEnd();
       }
-      ABSL_FALLTHROUGH_INTENDED;
-    }
-    default: {
-      continue_processing = visitor_->OnUnknownFrameEnd();
       break;
     }
+    case static_cast<uint64_t>(HttpFrameType::ACCEPT_CH): {
+      if (GetQuicReloadableFlag(quic_parse_accept_ch_frame)) {
+        // TODO(bnc): Avoid buffering if the entire frame is present, and
+        // instead parse directly out of |reader|.
+        AcceptChFrame frame;
+        QuicDataReader reader(buffer_.data(), current_frame_length_);
+        if (!ParseAcceptChFrame(&reader, &frame)) {
+          return false;
+        }
+        continue_processing = visitor_->OnAcceptChFrame(frame);
+      } else {
+        continue_processing = visitor_->OnUnknownFrameEnd();
+      }
+      break;
+    }
+    default:
+      continue_processing = visitor_->OnUnknownFrameEnd();
   }
 
   current_length_field_length_ = 0;
@@ -508,6 +537,17 @@
   return continue_processing;
 }
 
+bool HttpDecoder::HandleUnknownFramePayload(QuicDataReader* reader) {
+  QuicByteCount bytes_to_read = std::min<QuicByteCount>(
+      remaining_frame_length_, reader->BytesRemaining());
+  absl::string_view payload;
+  bool success = reader->ReadStringPiece(&payload, bytes_to_read);
+  DCHECK(success);
+  DCHECK(!payload.empty());
+  remaining_frame_length_ -= payload.length();
+  return visitor_->OnUnknownFramePayload(payload);
+}
+
 void HttpDecoder::DiscardFramePayload(QuicDataReader* reader) {
   QuicByteCount bytes_to_read = std::min<QuicByteCount>(
       remaining_frame_length_, reader->BytesRemaining());
@@ -647,6 +687,26 @@
   return true;
 }
 
+bool HttpDecoder::ParseAcceptChFrame(QuicDataReader* reader,
+                                     AcceptChFrame* frame) {
+  absl::string_view origin;
+  absl::string_view value;
+  while (!reader->IsDoneReading()) {
+    if (!reader->ReadStringPieceVarInt62(&origin)) {
+      RaiseError(QUIC_HTTP_FRAME_ERROR, "Unable to read ACCEPT_CH origin.");
+      return false;
+    }
+    if (!reader->ReadStringPieceVarInt62(&value)) {
+      RaiseError(QUIC_HTTP_FRAME_ERROR, "Unable to read ACCEPT_CH value.");
+      return false;
+    }
+    // Copy data.
+    frame->entries.push_back({std::string(origin.data(), origin.size()),
+                              std::string(value.data(), value.size())});
+  }
+  return true;
+}
+
 QuicByteCount HttpDecoder::MaxFrameLength(uint64_t frame_type) {
   switch (frame_type) {
     case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH):
@@ -664,6 +724,9 @@
     case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM):
       // This limit is arbitrary.
       return 1024 * 1024;
+    case static_cast<uint64_t>(HttpFrameType::ACCEPT_CH):
+      // This limit is arbitrary.
+      return 1024 * 1024;
     default:
       // Other frames require no data buffering, so it's safe to have no limit.
       return std::numeric_limits<QuicByteCount>::max();
diff --git a/quic/core/http/http_decoder.h b/quic/core/http/http_decoder.h
index 479f444..86f3f58 100644
--- a/quic/core/http/http_decoder.h
+++ b/quic/core/http/http_decoder.h
@@ -102,6 +102,13 @@
     // Called when a PRIORITY_UPDATE frame has been successfully parsed.
     virtual bool OnPriorityUpdateFrame(const PriorityUpdateFrame& frame) = 0;
 
+    // Called when an ACCEPT_CH frame has been received.
+    // |header_length| contains ACCEPT_CH frame length and payload length.
+    virtual bool OnAcceptChFrameStart(QuicByteCount header_length) = 0;
+
+    // Called when an ACCEPT_CH frame has been successfully parsed.
+    virtual bool OnAcceptChFrame(const AcceptChFrame& frame) = 0;
+
     // Called when a frame of unknown type |frame_type| has been received.
     // Frame type might be reserved, Visitor must make sure to ignore.
     // |header_length| and |payload_length| are the length of the frame header
@@ -174,6 +181,11 @@
   // had been parsed completely.  Returns whether processing should continue.
   bool FinishParsing();
 
+  // Read payload of unknown frame from |reader| and call
+  // Visitor::OnUnknownFramePayload().  Returns true decoding should continue,
+  // false if it should be paused.
+  bool HandleUnknownFramePayload(QuicDataReader* reader);
+
   // Discards any remaining frame payload from |reader|.
   void DiscardFramePayload(QuicDataReader* reader);
 
@@ -207,6 +219,9 @@
   bool ParseNewPriorityUpdateFrame(QuicDataReader* reader,
                                    PriorityUpdateFrame* frame);
 
+  // Parses the payload of an ACCEPT_CH frame from |reader| into |frame|.
+  bool ParseAcceptChFrame(QuicDataReader* reader, AcceptChFrame* frame);
+
   // Returns the max frame size of a given |frame_type|.
   QuicByteCount MaxFrameLength(uint64_t frame_type);
 
diff --git a/quic/core/http/http_decoder_test.cc b/quic/core/http/http_decoder_test.cc
index 68f9d7f..68248ea 100644
--- a/quic/core/http/http_decoder_test.cc
+++ b/quic/core/http/http_decoder_test.cc
@@ -21,6 +21,7 @@
 #include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h"
 
 using ::testing::_;
+using ::testing::AnyNumber;
 using ::testing::Eq;
 using ::testing::InSequence;
 using ::testing::Return;
@@ -104,6 +105,12 @@
               (override));
 
   MOCK_METHOD(bool,
+              OnAcceptChFrameStart,
+              (QuicByteCount header_length),
+              (override));
+  MOCK_METHOD(bool, OnAcceptChFrame, (const AcceptChFrame& frame), (override));
+
+  MOCK_METHOD(bool,
               OnUnknownFrameStart,
               (uint64_t frame_type,
                QuicByteCount header_length,
@@ -138,6 +145,8 @@
     ON_CALL(visitor_, OnPriorityUpdateFrameStart(_))
         .WillByDefault(Return(true));
     ON_CALL(visitor_, OnPriorityUpdateFrame(_)).WillByDefault(Return(true));
+    ON_CALL(visitor_, OnAcceptChFrameStart(_)).WillByDefault(Return(true));
+    ON_CALL(visitor_, OnAcceptChFrame(_)).WillByDefault(Return(true));
     ON_CALL(visitor_, OnUnknownFrameStart(_, _, _)).WillByDefault(Return(true));
     ON_CALL(visitor_, OnUnknownFramePayload(_)).WillByDefault(Return(true));
     ON_CALL(visitor_, OnUnknownFrameEnd()).WillByDefault(Return(true));
@@ -1192,6 +1201,153 @@
   }
 }
 
+TEST_F(HttpDecoderTest, AcceptChFrame) {
+  if (!GetQuicReloadableFlag(quic_parse_accept_ch_frame)) {
+    return;
+  }
+
+  InSequence s;
+  std::string input1 = absl::HexStringToBytes(
+      "4089"  // type (ACCEPT_CH)
+      "00");  // length
+
+  AcceptChFrame accept_ch1;
+
+  // Visitor pauses processing.
+  EXPECT_CALL(visitor_, OnAcceptChFrameStart(3)).WillOnce(Return(false));
+  absl::string_view remaining_input(input1);
+  QuicByteCount processed_bytes =
+      ProcessInputWithGarbageAppended(remaining_input);
+  EXPECT_EQ(3u, processed_bytes);
+  remaining_input = remaining_input.substr(processed_bytes);
+
+  EXPECT_CALL(visitor_, OnAcceptChFrame(accept_ch1)).WillOnce(Return(false));
+  processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
+  EXPECT_EQ(remaining_input.size(), processed_bytes);
+  EXPECT_THAT(decoder_.error(), IsQuicNoError());
+  EXPECT_EQ("", decoder_.error_detail());
+
+  // Process the full frame.
+  EXPECT_CALL(visitor_, OnAcceptChFrameStart(3));
+  EXPECT_CALL(visitor_, OnAcceptChFrame(accept_ch1));
+  EXPECT_EQ(input1.size(), ProcessInput(input1));
+  EXPECT_THAT(decoder_.error(), IsQuicNoError());
+  EXPECT_EQ("", decoder_.error_detail());
+
+  // Process the frame incrementally.
+  EXPECT_CALL(visitor_, OnAcceptChFrameStart(3));
+  EXPECT_CALL(visitor_, OnAcceptChFrame(accept_ch1));
+  ProcessInputCharByChar(input1);
+  EXPECT_THAT(decoder_.error(), IsQuicNoError());
+  EXPECT_EQ("", decoder_.error_detail());
+
+  std::string input2 = absl::HexStringToBytes(
+      "4089"      // type (ACCEPT_CH)
+      "08"        // length
+      "03"        // length of origin
+      "666f6f"    // origin "foo"
+      "03"        // length of value
+      "626172");  // value "bar"
+
+  AcceptChFrame accept_ch2;
+  accept_ch2.entries.push_back({"foo", "bar"});
+
+  // Visitor pauses processing.
+  EXPECT_CALL(visitor_, OnAcceptChFrameStart(3)).WillOnce(Return(false));
+  remaining_input = input2;
+  processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
+  EXPECT_EQ(3u, processed_bytes);
+  remaining_input = remaining_input.substr(processed_bytes);
+
+  EXPECT_CALL(visitor_, OnAcceptChFrame(accept_ch2)).WillOnce(Return(false));
+  processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
+  EXPECT_EQ(remaining_input.size(), processed_bytes);
+  EXPECT_THAT(decoder_.error(), IsQuicNoError());
+  EXPECT_EQ("", decoder_.error_detail());
+
+  // Process the full frame.
+  EXPECT_CALL(visitor_, OnAcceptChFrameStart(3));
+  EXPECT_CALL(visitor_, OnAcceptChFrame(accept_ch2));
+  EXPECT_EQ(input2.size(), ProcessInput(input2));
+  EXPECT_THAT(decoder_.error(), IsQuicNoError());
+  EXPECT_EQ("", decoder_.error_detail());
+
+  // Process the frame incrementally.
+  EXPECT_CALL(visitor_, OnAcceptChFrameStart(3));
+  EXPECT_CALL(visitor_, OnAcceptChFrame(accept_ch2));
+  ProcessInputCharByChar(input2);
+  EXPECT_THAT(decoder_.error(), IsQuicNoError());
+  EXPECT_EQ("", decoder_.error_detail());
+}
+
+TEST_F(HttpDecoderTest, CorruptAcceptChFrame) {
+  if (!GetQuicReloadableFlag(quic_parse_accept_ch_frame)) {
+    // TODO(bnc): merge this test into HttpDecoderTest.CorruptFrame when
+    // deprecating flag.
+    return;
+  }
+
+  EXPECT_CALL(visitor_, OnAcceptChFrameStart(_)).Times(AnyNumber());
+
+  struct {
+    const char* const input;
+    const char* const error_message;
+  } kTestData[] = {{"\x40\x89"  // type (ACCEPT_CH)
+                    "\x01"      // length
+                    "\x40",     // first byte of two-byte varint origin length
+                    "Unable to read ACCEPT_CH origin."},
+                   {"\x40\x89"  // type (ACCEPT_CH)
+                    "\x01"      // length
+                    "\x05",     // valid origin length but no origin string
+                    "Unable to read ACCEPT_CH origin."},
+                   {"\x40\x89"  // type (ACCEPT_CH)
+                    "\x04"      // length
+                    "\x05"      // valid origin length
+                    "foo",      // payload ends before origin ends
+                    "Unable to read ACCEPT_CH origin."},
+                   {"\x40\x89"  // type (ACCEPT_CH)
+                    "\x04"      // length
+                    "\x03"      // valid origin length
+                    "foo",      // payload ends at end of origin: no value
+                    "Unable to read ACCEPT_CH value."},
+                   {"\x40\x89"  // type (ACCEPT_CH)
+                    "\x05"      // length
+                    "\x03"      // valid origin length
+                    "foo"       // payload ends at end of origin: no value
+                    "\x40",     // first byte of two-byte varint value length
+                    "Unable to read ACCEPT_CH value."},
+                   {"\x40\x89"  // type (ACCEPT_CH)
+                    "\x08"      // length
+                    "\x03"      // valid origin length
+                    "foo"       // origin
+                    "\x05"      // valid value length
+                    "bar",      // payload ends before value ends
+                    "Unable to read ACCEPT_CH value."}};
+
+  for (const auto& test_data : kTestData) {
+    {
+      HttpDecoder decoder(&visitor_);
+      EXPECT_CALL(visitor_, OnError(&decoder));
+
+      absl::string_view input(test_data.input);
+      decoder.ProcessInput(input.data(), input.size());
+      EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR));
+      EXPECT_EQ(test_data.error_message, decoder.error_detail());
+    }
+    {
+      HttpDecoder decoder(&visitor_);
+      EXPECT_CALL(visitor_, OnError(&decoder));
+
+      absl::string_view input(test_data.input);
+      for (auto c : input) {
+        decoder.ProcessInput(&c, 1);
+      }
+      EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR));
+      EXPECT_EQ(test_data.error_message, decoder.error_detail());
+    }
+  }
+}
+
 TEST_F(HttpDecoderTest, DecodeSettings) {
   std::string input = absl::HexStringToBytes(
       "04"    // type (SETTINGS)
diff --git a/quic/core/http/quic_receive_control_stream.cc b/quic/core/http/quic_receive_control_stream.cc
index cc6857f..17c588d 100644
--- a/quic/core/http/quic_receive_control_stream.cc
+++ b/quic/core/http/quic_receive_control_stream.cc
@@ -252,6 +252,34 @@
   return true;
 }
 
+bool QuicReceiveControlStream::OnAcceptChFrameStart(
+    QuicByteCount /* header_length */) {
+  if (!settings_frame_received_) {
+    stream_delegate()->OnStreamError(
+        QUIC_HTTP_MISSING_SETTINGS_FRAME,
+        "ACCEPT_CH frame received before SETTINGS.");
+    return false;
+  }
+
+  if (spdy_session()->perspective() == Perspective::IS_SERVER) {
+    OnWrongFrame("ACCEPT_CH");
+    return false;
+  }
+
+  return true;
+}
+
+bool QuicReceiveControlStream::OnAcceptChFrame(const AcceptChFrame& frame) {
+  DCHECK_EQ(Perspective::IS_CLIENT, spdy_session()->perspective());
+
+  if (spdy_session()->debug_visitor()) {
+    spdy_session()->debug_visitor()->OnAcceptChFrameReceived(frame);
+  }
+
+  spdy_session()->OnAcceptChFrame(frame);
+  return true;
+}
+
 bool QuicReceiveControlStream::OnUnknownFrameStart(
     uint64_t frame_type,
     QuicByteCount /*header_length*/,
diff --git a/quic/core/http/quic_receive_control_stream.h b/quic/core/http/quic_receive_control_stream.h
index 2654bb8..728aa31 100644
--- a/quic/core/http/quic_receive_control_stream.h
+++ b/quic/core/http/quic_receive_control_stream.h
@@ -56,6 +56,8 @@
   bool OnPushPromiseFrameEnd() override;
   bool OnPriorityUpdateFrameStart(QuicByteCount header_length) override;
   bool OnPriorityUpdateFrame(const PriorityUpdateFrame& frame) override;
+  bool OnAcceptChFrameStart(QuicByteCount header_length) override;
+  bool OnAcceptChFrame(const AcceptChFrame& frame) override;
   bool OnUnknownFrameStart(uint64_t frame_type,
                            QuicByteCount header_length,
                            QuicByteCount payload_length) override;
diff --git a/quic/core/http/quic_receive_control_stream_test.cc b/quic/core/http/quic_receive_control_stream_test.cc
index 51948e6..949a0f4 100644
--- a/quic/core/http/quic_receive_control_stream_test.cc
+++ b/quic/core/http/quic_receive_control_stream_test.cc
@@ -396,6 +396,70 @@
                       /* offset = */ 1, cancel_push_frame));
 }
 
+TEST_P(QuicReceiveControlStreamTest, AcceptChFrameBeforeSettings) {
+  std::string accept_ch_frame = absl::HexStringToBytes(
+      "4089"  // type (ACCEPT_CH)
+      "00");  // length
+
+  EXPECT_CALL(*connection_,
+              CloseConnection(QUIC_HTTP_MISSING_SETTINGS_FRAME,
+                              GetQuicReloadableFlag(quic_parse_accept_ch_frame)
+                                  ? "ACCEPT_CH frame received before SETTINGS."
+                                  : "Unknown frame received before SETTINGS.",
+                              _))
+      .WillOnce(
+          Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
+  EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
+  EXPECT_CALL(session_, OnConnectionClosed(_, _));
+
+  receive_control_stream_->OnStreamFrame(
+      QuicStreamFrame(receive_control_stream_->id(), /* fin = */ false,
+                      /* offset = */ 1, accept_ch_frame));
+}
+
+TEST_P(QuicReceiveControlStreamTest, ReceiveAcceptChFrame) {
+  StrictMock<MockHttp3DebugVisitor> debug_visitor;
+  session_.set_debug_visitor(&debug_visitor);
+
+  const QuicStreamId id = receive_control_stream_->id();
+  QuicStreamOffset offset = 1;
+
+  // Receive SETTINGS frame.
+  SettingsFrame settings;
+  std::string settings_frame = EncodeSettings(settings);
+  EXPECT_CALL(debug_visitor, OnSettingsFrameReceived(settings));
+  receive_control_stream_->OnStreamFrame(
+      QuicStreamFrame(id, /* fin = */ false, offset, settings_frame));
+  offset += settings_frame.length();
+
+  // Receive ACCEPT_CH frame.
+  std::string accept_ch_frame = absl::HexStringToBytes(
+      "4089"  // type (ACCEPT_CH)
+      "00");  // length
+
+  if (GetQuicReloadableFlag(quic_parse_accept_ch_frame)) {
+    if (perspective() == Perspective::IS_CLIENT) {
+      EXPECT_CALL(debug_visitor, OnAcceptChFrameReceived(_));
+    } else {
+      EXPECT_CALL(
+          *connection_,
+          CloseConnection(QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM,
+                          "ACCEPT_CH frame received on control stream", _))
+          .WillOnce(
+              Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
+      EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
+      EXPECT_CALL(session_, OnConnectionClosed(_, _));
+    }
+  } else {
+    EXPECT_CALL(debug_visitor,
+                OnUnknownFrameReceived(id, /* frame_type = */ 0x89,
+                                       /* payload_length = */ 0));
+  }
+
+  receive_control_stream_->OnStreamFrame(
+      QuicStreamFrame(id, /* fin = */ false, offset, accept_ch_frame));
+}
+
 TEST_P(QuicReceiveControlStreamTest, UnknownFrameBeforeSettings) {
   std::string unknown_frame = absl::HexStringToBytes(
       "21"        // reserved frame type
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h
index 4ab4cfd..a90befa 100644
--- a/quic/core/http/quic_spdy_session.h
+++ b/quic/core/http/quic_spdy_session.h
@@ -90,6 +90,7 @@
   virtual void OnMaxPushIdFrameReceived(const MaxPushIdFrame& /*frame*/) {}
   virtual void OnPriorityUpdateFrameReceived(
       const PriorityUpdateFrame& /*frame*/) {}
+  virtual void OnAcceptChFrameReceived(const AcceptChFrame& /*frame*/) {}
 
   // Incoming HTTP/3 frames on request or push streams.
   virtual void OnDataFrameReceived(QuicStreamId /*stream_id*/,
@@ -196,6 +197,10 @@
   // stream.  Returns false and closes connection if |push_id| is invalid.
   bool OnPriorityUpdateForPushStream(QuicStreamId push_id, int urgency);
 
+  // Called when an HTTP/3 ACCEPT_CH frame has been received.
+  // This method will only be called for client sessions.
+  virtual void OnAcceptChFrame(const AcceptChFrame& /*frame*/) {}
+
   // Sends contents of |iov| to h2_deframer_, returns number of bytes processed.
   size_t ProcessHeaderData(const struct iovec& iov);
 
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index 3606949..e4b5dc0 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -351,6 +351,8 @@
                       GetEncryptionLevelToSendApplicationData());
   }
 
+  MOCK_METHOD(void, OnAcceptChFrame, (const AcceptChFrame&), (override));
+
   using QuicSession::closed_streams;
   using QuicSession::ShouldKeepConnectionAlive;
   using QuicSpdySession::ProcessPendingStream;
@@ -3260,6 +3262,57 @@
   session_.OnSettingsFrame(frame);
 }
 
+TEST_P(QuicSpdySessionTestClient, ReceiveAcceptChFrame) {
+  if (!VersionUsesHttp3(transport_version())) {
+    return;
+  }
+
+  if (!GetQuicReloadableFlag(quic_parse_accept_ch_frame)) {
+    return;
+  }
+
+  StrictMock<MockHttp3DebugVisitor> debug_visitor;
+  session_.set_debug_visitor(&debug_visitor);
+
+  // Create control stream.
+  QuicStreamId receive_control_stream_id =
+      GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 3);
+  char type[] = {kControlStream};
+  absl::string_view stream_type(type, 1);
+  QuicStreamOffset offset = 0;
+  QuicStreamFrame data1(receive_control_stream_id, /* fin = */ false, offset,
+                        stream_type);
+  offset += stream_type.length();
+  EXPECT_CALL(debug_visitor,
+              OnPeerControlStreamCreated(receive_control_stream_id));
+
+  session_.OnStreamFrame(data1);
+  EXPECT_EQ(receive_control_stream_id,
+            QuicSpdySessionPeer::GetReceiveControlStream(&session_)->id());
+
+  // First frame has to be SETTINGS.
+  std::string serialized_settings = EncodeSettings({});
+  QuicStreamFrame data2(receive_control_stream_id, /* fin = */ false, offset,
+                        serialized_settings);
+  offset += serialized_settings.length();
+  EXPECT_CALL(debug_visitor, OnSettingsFrameReceived(_));
+
+  session_.OnStreamFrame(data2);
+
+  // Receive ACCEPT_CH frame.
+  AcceptChFrame accept_ch;
+  accept_ch.entries.push_back({"foo", "bar"});
+  std::unique_ptr<char[]> buffer;
+  auto frame_length = HttpEncoder::SerializeAcceptChFrame(accept_ch, &buffer);
+  QuicStreamFrame data3(receive_control_stream_id, /* fin = */ false, offset,
+                        absl::string_view(buffer.get(), frame_length));
+
+  EXPECT_CALL(debug_visitor, OnAcceptChFrameReceived(accept_ch));
+  EXPECT_CALL(session_, OnAcceptChFrame(accept_ch));
+
+  session_.OnStreamFrame(data3);
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/http/quic_spdy_stream.cc b/quic/core/http/quic_spdy_stream.cc
index 62a81ae..8489954 100644
--- a/quic/core/http/quic_spdy_stream.cc
+++ b/quic/core/http/quic_spdy_stream.cc
@@ -154,6 +154,16 @@
     return false;
   }
 
+  bool OnAcceptChFrameStart(QuicByteCount /*header_length*/) override {
+    CloseConnectionOnWrongFrame("ACCEPT_CH");
+    return false;
+  }
+
+  bool OnAcceptChFrame(const AcceptChFrame& /*frame*/) override {
+    CloseConnectionOnWrongFrame("ACCEPT_CH");
+    return false;
+  }
+
   bool OnUnknownFrameStart(uint64_t frame_type,
                            QuicByteCount header_length,
                            QuicByteCount payload_length) override {
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 5d26ab0..fb58ae8 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -9821,6 +9821,10 @@
   }
 
   // These values come from draft-ietf-quic-tls Appendix A.4.
+  char retry_packet_rfcv1[] = {
+      0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a,
+      0x42, 0x62, 0xb5, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x04, 0xa2, 0x65, 0xba,
+      0x2e, 0xff, 0x4d, 0x82, 0x90, 0x58, 0xfb, 0x3f, 0x0f, 0x24, 0x96, 0xba};
   char retry_packet29[] = {
       0xff, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a,
       0x42, 0x62, 0xb5, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0xd1, 0x69, 0x26, 0xd8,
@@ -9828,7 +9832,10 @@
 
   char* retry_packet;
   size_t retry_packet_length;
-  if (version() == ParsedQuicVersion::Draft29()) {
+  if (version() == ParsedQuicVersion::RFCv1()) {
+    retry_packet = retry_packet_rfcv1;
+    retry_packet_length = ABSL_ARRAYSIZE(retry_packet_rfcv1);
+  } else if (version() == ParsedQuicVersion::Draft29()) {
     retry_packet = retry_packet29;
     retry_packet_length = ABSL_ARRAYSIZE(retry_packet29);
   } else {
diff --git a/quic/core/quic_dispatcher_test.cc b/quic/core/quic_dispatcher_test.cc
index d65159f..5810c42 100644
--- a/quic/core/quic_dispatcher_test.cc
+++ b/quic/core/quic_dispatcher_test.cc
@@ -1285,7 +1285,7 @@
                              received_packet44);
 }
 
-static_assert(quic::SupportedVersions().size() == 5u,
+static_assert(quic::SupportedVersions().size() == 6u,
               "Please add new RejectDeprecatedVersion tests above this assert "
               "when deprecating versions");
 
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 28082aa..fcacf5d 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -37,6 +37,7 @@
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_mtu_discovery_at_server, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_server_on_wire_ping, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_token_based_address_validation, false)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_version_rfcv1, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_encrypted_control_frames, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_encrypted_goaway, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_extract_x509_subject_using_certificate_view, true)
@@ -44,6 +45,7 @@
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_goaway_with_max_stream_id, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_granular_qpack_error_codes, true)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_new_priority_update_frame, true)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_parse_accept_ch_frame, true)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_pass_path_response_to_validator, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_require_handshake_confirmation, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_round_up_tiny_bandwidth, true)
diff --git a/quic/core/quic_trace_visitor_test.cc b/quic/core/quic_trace_visitor_test.cc
index 07bc4f2..9a63d45 100644
--- a/quic/core/quic_trace_visitor_test.cc
+++ b/quic/core/quic_trace_visitor_test.cc
@@ -81,7 +81,6 @@
 TEST_F(QuicTraceVisitorTest, Version) {
   std::string version = trace_.protocol_version();
   ASSERT_EQ(4u, version.size());
-  EXPECT_NE(0, version[0]);
 }
 
 // Check that basic metadata about sent packets is recorded.
diff --git a/quic/core/quic_version_manager.cc b/quic/core/quic_version_manager.cc
index 946e152..c80b95b 100644
--- a/quic/core/quic_version_manager.cc
+++ b/quic/core/quic_version_manager.cc
@@ -15,14 +15,15 @@
 
 QuicVersionManager::QuicVersionManager(
     ParsedQuicVersionVector supported_versions)
-    : disable_version_draft_29_(
+    : enable_version_rfcv1_(GetQuicReloadableFlag(quic_enable_version_rfcv1)),
+      disable_version_draft_29_(
           GetQuicReloadableFlag(quic_disable_version_draft_29)),
       disable_version_t051_(GetQuicReloadableFlag(quic_disable_version_t051)),
       disable_version_q050_(GetQuicReloadableFlag(quic_disable_version_q050)),
       disable_version_q046_(GetQuicReloadableFlag(quic_disable_version_q046)),
       disable_version_q043_(GetQuicReloadableFlag(quic_disable_version_q043)),
       allowed_supported_versions_(std::move(supported_versions)) {
-  static_assert(SupportedVersions().size() == 5u,
+  static_assert(SupportedVersions().size() == 6u,
                 "Supported versions out of sync");
   RefilterSupportedVersions();
 }
@@ -46,9 +47,11 @@
 }
 
 void QuicVersionManager::MaybeRefilterSupportedVersions() {
-  static_assert(SupportedVersions().size() == 5u,
+  static_assert(SupportedVersions().size() == 6u,
                 "Supported versions out of sync");
-  if (disable_version_draft_29_ !=
+  if (enable_version_rfcv1_ !=
+          GetQuicReloadableFlag(quic_enable_version_rfcv1) ||
+      disable_version_draft_29_ !=
           GetQuicReloadableFlag(quic_disable_version_draft_29) ||
       disable_version_t051_ !=
           GetQuicReloadableFlag(quic_disable_version_t051) ||
@@ -58,6 +61,7 @@
           GetQuicReloadableFlag(quic_disable_version_q046) ||
       disable_version_q043_ !=
           GetQuicReloadableFlag(quic_disable_version_q043)) {
+    enable_version_rfcv1_ = GetQuicReloadableFlag(quic_enable_version_rfcv1);
     disable_version_draft_29_ =
         GetQuicReloadableFlag(quic_disable_version_draft_29);
     disable_version_t051_ = GetQuicReloadableFlag(quic_disable_version_t051);
diff --git a/quic/core/quic_version_manager.h b/quic/core/quic_version_manager.h
index d38019c..b5712c6 100644
--- a/quic/core/quic_version_manager.h
+++ b/quic/core/quic_version_manager.h
@@ -50,6 +50,8 @@
 
  private:
   // Cached value of reloadable flags.
+  // quic_enable_version_rfcv1 flag
+  bool enable_version_rfcv1_;
   // quic_disable_version_draft_29 flag
   bool disable_version_draft_29_;
   // quic_disable_version_t051 flag
diff --git a/quic/core/quic_version_manager_test.cc b/quic/core/quic_version_manager_test.cc
index b1a67ee..56af975 100644
--- a/quic/core/quic_version_manager_test.cc
+++ b/quic/core/quic_version_manager_test.cc
@@ -18,11 +18,12 @@
 class QuicVersionManagerTest : public QuicTest {};
 
 TEST_F(QuicVersionManagerTest, QuicVersionManager) {
-  static_assert(SupportedVersions().size() == 5u,
+  static_assert(SupportedVersions().size() == 6u,
                 "Supported versions out of sync");
   for (const ParsedQuicVersion& version : AllSupportedVersions()) {
     QuicEnableVersion(version);
   }
+  QuicDisableVersion(ParsedQuicVersion::RFCv1());
   QuicDisableVersion(ParsedQuicVersion::Draft29());
   QuicDisableVersion(ParsedQuicVersion::T051());
   QuicVersionManager manager(AllSupportedVersions());
diff --git a/quic/core/quic_versions.cc b/quic/core/quic_versions.cc
index 27d56c9..46a092e 100644
--- a/quic/core/quic_versions.cc
+++ b/quic/core/quic_versions.cc
@@ -42,11 +42,13 @@
 }
 
 void SetVersionFlag(const ParsedQuicVersion& version, bool should_enable) {
-  static_assert(SupportedVersions().size() == 5u,
+  static_assert(SupportedVersions().size() == 6u,
                 "Supported versions out of sync");
   const bool enable = should_enable;
   const bool disable = !should_enable;
-  if (version == ParsedQuicVersion::Draft29()) {
+  if (version == ParsedQuicVersion::RFCv1()) {
+    SetQuicReloadableFlag(quic_enable_version_rfcv1, enable);
+  } else if (version == ParsedQuicVersion::Draft29()) {
     SetQuicReloadableFlag(quic_disable_version_draft_29, disable);
   } else if (version == ParsedQuicVersion::T051()) {
     SetQuicReloadableFlag(quic_disable_version_t051, disable);
@@ -167,6 +169,11 @@
   return VersionHasIetfQuicFrames(transport_version);
 }
 
+bool ParsedQuicVersion::UsesLegacyTlsExtension() const {
+  DCHECK(IsKnown());
+  return transport_version <= QUIC_VERSION_IETF_DRAFT_29;
+}
+
 bool ParsedQuicVersion::UsesTls() const {
   DCHECK(IsKnown());
   return handshake_protocol == PROTOCOL_TLS1_3;
@@ -208,9 +215,11 @@
 }
 
 QuicVersionLabel CreateQuicVersionLabel(ParsedQuicVersion parsed_version) {
-  static_assert(SupportedVersions().size() == 5u,
+  static_assert(SupportedVersions().size() == 6u,
                 "Supported versions out of sync");
-  if (parsed_version == ParsedQuicVersion::Draft29()) {
+  if (parsed_version == ParsedQuicVersion::RFCv1()) {
+    return MakeVersionLabel(0x00, 0x00, 0x00, 0x01);
+  } else if (parsed_version == ParsedQuicVersion::Draft29()) {
     return MakeVersionLabel(0xff, 0x00, 0x00, 29);
   } else if (parsed_version == ParsedQuicVersion::T051()) {
     return MakeVersionLabel('T', '0', '5', '1');
@@ -380,7 +389,11 @@
   ParsedQuicVersionVector filtered_versions;
   filtered_versions.reserve(versions.size());
   for (const ParsedQuicVersion& version : versions) {
-    if (version == ParsedQuicVersion::Draft29()) {
+    if (version == ParsedQuicVersion::RFCv1()) {
+      if (GetQuicReloadableFlag(quic_enable_version_rfcv1)) {
+        filtered_versions.push_back(version);
+      }
+    } else if (version == ParsedQuicVersion::Draft29()) {
       if (!GetQuicReloadableFlag(quic_disable_version_draft_29)) {
         filtered_versions.push_back(version);
       }
@@ -455,6 +468,7 @@
     RETURN_STRING_LITERAL(QUIC_VERSION_50);
     RETURN_STRING_LITERAL(QUIC_VERSION_51);
     RETURN_STRING_LITERAL(QUIC_VERSION_IETF_DRAFT_29);
+    RETURN_STRING_LITERAL(QUIC_VERSION_IETF_RFC_V1);
     RETURN_STRING_LITERAL(QUIC_VERSION_UNSUPPORTED);
     RETURN_STRING_LITERAL(QUIC_VERSION_RESERVED_FOR_NEGOTIATION);
   }
@@ -473,12 +487,14 @@
 }
 
 std::string ParsedQuicVersionToString(ParsedQuicVersion version) {
-  static_assert(SupportedVersions().size() == 5u,
+  static_assert(SupportedVersions().size() == 6u,
                 "Supported versions out of sync");
   if (version == UnsupportedQuicVersion()) {
     return "0";
-  }
-  if (version == ParsedQuicVersion::Draft29()) {
+  } else if (version == ParsedQuicVersion::RFCv1()) {
+    DCHECK(version.UsesHttp3());
+    return "RFCv1";
+  } else if (version == ParsedQuicVersion::Draft29()) {
     DCHECK(version.UsesHttp3());
     return "draft29";
   }
@@ -567,7 +583,9 @@
 }
 
 std::string AlpnForVersion(ParsedQuicVersion parsed_version) {
-  if (parsed_version == ParsedQuicVersion::Draft29()) {
+  if (parsed_version == ParsedQuicVersion::RFCv1()) {
+    return "h3";
+  } else if (parsed_version == ParsedQuicVersion::Draft29()) {
     return "h3-29";
   }
   return "h3-" + ParsedQuicVersionToString(parsed_version);
diff --git a/quic/core/quic_versions.h b/quic/core/quic_versions.h
index 3257cc5..9bb58a5 100644
--- a/quic/core/quic_versions.h
+++ b/quic/core/quic_versions.h
@@ -123,6 +123,7 @@
   // Number 71 used to represent draft-ietf-quic-transport-27.
   // Number 72 used to represent draft-ietf-quic-transport-28.
   QUIC_VERSION_IETF_DRAFT_29 = 73,  // draft-ietf-quic-transport-29.
+  QUIC_VERSION_IETF_RFC_V1 = 80,    // Not-yet-published RFC.
   // Version 99 was a dumping ground for IETF QUIC changes which were not yet
   // yet ready for production between 2018-02 and 2020-02.
 
@@ -170,6 +171,7 @@
     QuicTransportVersion transport_version) {
   bool transport_version_is_valid = false;
   constexpr QuicTransportVersion valid_transport_versions[] = {
+      QUIC_VERSION_IETF_RFC_V1,
       QUIC_VERSION_IETF_DRAFT_29,
       QUIC_VERSION_51,
       QUIC_VERSION_50,
@@ -194,7 +196,8 @@
       return transport_version != QUIC_VERSION_UNSUPPORTED &&
              transport_version != QUIC_VERSION_RESERVED_FOR_NEGOTIATION &&
              transport_version != QUIC_VERSION_51 &&
-             transport_version != QUIC_VERSION_IETF_DRAFT_29;
+             transport_version != QUIC_VERSION_IETF_DRAFT_29 &&
+             transport_version != QUIC_VERSION_IETF_RFC_V1;
     case PROTOCOL_TLS1_3:
       return transport_version != QUIC_VERSION_UNSUPPORTED &&
              transport_version != QUIC_VERSION_50 &&
@@ -243,6 +246,10 @@
            transport_version != other.transport_version;
   }
 
+  static constexpr ParsedQuicVersion RFCv1() {
+    return ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_RFC_V1);
+  }
+
   static constexpr ParsedQuicVersion Draft29() {
     return ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_29);
   }
@@ -351,6 +358,9 @@
   // frames or not.
   bool HasIetfQuicFrames() const;
 
+  // Returns whether this version uses the legacy TLS extension codepoint.
+  bool UsesLegacyTlsExtension() const;
+
   // Returns whether this version uses PROTOCOL_TLS1_3.
   bool UsesTls() const;
 
@@ -389,11 +399,11 @@
   return {PROTOCOL_TLS1_3, PROTOCOL_QUIC_CRYPTO};
 }
 
-constexpr std::array<ParsedQuicVersion, 5> SupportedVersions() {
+constexpr std::array<ParsedQuicVersion, 6> SupportedVersions() {
   return {
-      ParsedQuicVersion::Draft29(), ParsedQuicVersion::T051(),
-      ParsedQuicVersion::Q050(),    ParsedQuicVersion::Q046(),
-      ParsedQuicVersion::Q043(),
+      ParsedQuicVersion::RFCv1(), ParsedQuicVersion::Draft29(),
+      ParsedQuicVersion::T051(),  ParsedQuicVersion::Q050(),
+      ParsedQuicVersion::Q046(),  ParsedQuicVersion::Q043(),
   };
 }
 
diff --git a/quic/core/quic_versions_test.cc b/quic/core/quic_versions_test.cc
index d8af34f..e3bd906 100644
--- a/quic/core/quic_versions_test.cc
+++ b/quic/core/quic_versions_test.cc
@@ -371,23 +371,25 @@
 // yet a typo was made in doing the #defines and it was caught
 // only in some test far removed from here... Better safe than sorry.
 TEST_F(QuicVersionsTest, CheckTransportVersionNumbersForTypos) {
-  static_assert(SupportedVersions().size() == 5u,
+  static_assert(SupportedVersions().size() == 6u,
                 "Supported versions out of sync");
   EXPECT_EQ(QUIC_VERSION_43, 43);
   EXPECT_EQ(QUIC_VERSION_46, 46);
   EXPECT_EQ(QUIC_VERSION_50, 50);
   EXPECT_EQ(QUIC_VERSION_51, 51);
   EXPECT_EQ(QUIC_VERSION_IETF_DRAFT_29, 73);
+  EXPECT_EQ(QUIC_VERSION_IETF_RFC_V1, 80);
 }
 
 TEST_F(QuicVersionsTest, AlpnForVersion) {
-  static_assert(SupportedVersions().size() == 5u,
+  static_assert(SupportedVersions().size() == 6u,
                 "Supported versions out of sync");
   EXPECT_EQ("h3-Q043", AlpnForVersion(ParsedQuicVersion::Q043()));
   EXPECT_EQ("h3-Q046", AlpnForVersion(ParsedQuicVersion::Q046()));
   EXPECT_EQ("h3-Q050", AlpnForVersion(ParsedQuicVersion::Q050()));
   EXPECT_EQ("h3-T051", AlpnForVersion(ParsedQuicVersion::T051()));
   EXPECT_EQ("h3-29", AlpnForVersion(ParsedQuicVersion::Draft29()));
+  EXPECT_EQ("h3", AlpnForVersion(ParsedQuicVersion::RFCv1()));
 }
 
 TEST_F(QuicVersionsTest, QuicVersionEnabling) {
diff --git a/quic/core/tls_chlo_extractor.cc b/quic/core/tls_chlo_extractor.cc
index 27f44d3..62a8636 100644
--- a/quic/core/tls_chlo_extractor.cc
+++ b/quic/core/tls_chlo_extractor.cc
@@ -348,6 +348,13 @@
   const int rv = SSL_set_ex_data(ssl_.get(), ex_data_index, this);
   CHECK_EQ(rv, 1) << "Internal allocation failure in SSL_set_ex_data";
   SSL_set_accept_state(ssl_.get());
+
+  // Make sure we use the right TLS extension codepoint.
+  int use_legacy_extension = 0;
+  if (framer_->version().UsesLegacyTlsExtension()) {
+    use_legacy_extension = 1;
+  }
+  SSL_set_quic_use_legacy_codepoint(ssl_.get(), use_legacy_extension);
 }
 
 // Called by other methods to record any unrecoverable failures they experience.
diff --git a/quic/core/tls_client_handshaker.cc b/quic/core/tls_client_handshaker.cc
index 7e7c5fe..95be1fa 100644
--- a/quic/core/tls_client_handshaker.cc
+++ b/quic/core/tls_client_handshaker.cc
@@ -62,6 +62,13 @@
     return false;
   }
 
+  // Make sure we use the right TLS extension codepoint.
+  int use_legacy_extension = 0;
+  if (session()->version().UsesLegacyTlsExtension()) {
+    use_legacy_extension = 1;
+  }
+  SSL_set_quic_use_legacy_codepoint(ssl(), use_legacy_extension);
+
   // Set the SNI to send, if any.
   SSL_set_connect_state(ssl());
   if (QUIC_DLOG_INFO_IS_ON() &&
diff --git a/quic/core/tls_server_handshaker.cc b/quic/core/tls_server_handshaker.cc
index 0ae78a3..87b4213 100644
--- a/quic/core/tls_server_handshaker.cc
+++ b/quic/core/tls_server_handshaker.cc
@@ -100,6 +100,13 @@
   // Configure the SSL to be a server.
   SSL_set_accept_state(ssl());
 
+  // Make sure we use the right TLS extension codepoint.
+  int use_legacy_extension = 0;
+  if (session->version().UsesLegacyTlsExtension()) {
+    use_legacy_extension = 1;
+  }
+  SSL_set_quic_use_legacy_codepoint(ssl(), use_legacy_extension);
+
   if (GetQuicFlag(FLAGS_quic_disable_server_tls_resumption)) {
     SSL_set_options(ssl(), SSL_OP_NO_TICKET);
   }
@@ -281,12 +288,17 @@
   const uint8_t* client_params_bytes;
   size_t params_bytes_len;
   if (use_early_select_cert_) {
+    // Make sure we use the right TLS extension codepoint.
+    uint16_t extension_type = TLSEXT_TYPE_quic_transport_parameters;
+    if (session()->version().UsesLegacyTlsExtension()) {
+      extension_type = TLSEXT_TYPE_quic_transport_parameters_legacy;
+    }
     // When using early select cert callback, SSL_get_peer_quic_transport_params
     // can not be used to retrieve the client's transport parameters, but we can
     // use SSL_early_callback_ctx_extension_get to do that.
-    if (!SSL_early_callback_ctx_extension_get(
-            client_hello, TLSEXT_TYPE_quic_transport_parameters,
-            &client_params_bytes, &params_bytes_len)) {
+    if (!SSL_early_callback_ctx_extension_get(client_hello, extension_type,
+                                              &client_params_bytes,
+                                              &params_bytes_len)) {
       params_bytes_len = 0;
     }
   } else {
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
index 6fbcc17..50afd2d 100644
--- a/quic/test_tools/quic_test_utils.h
+++ b/quic/test_tools/quic_test_utils.h
@@ -1110,6 +1110,10 @@
               OnPriorityUpdateFrameReceived,
               (const PriorityUpdateFrame&),
               (override));
+  MOCK_METHOD(void,
+              OnAcceptChFrameReceived,
+              (const AcceptChFrame&),
+              (override));
 
   MOCK_METHOD(void,
               OnDataFrameReceived,
diff --git a/spdy/core/spdy_protocol.cc b/spdy/core/spdy_protocol.cc
index 2116025..b1d9f56 100644
--- a/spdy/core/spdy_protocol.cc
+++ b/spdy/core/spdy_protocol.cc
@@ -60,7 +60,31 @@
 }
 
 bool IsDefinedFrameType(uint8_t frame_type_field) {
-  return frame_type_field <= SerializeFrameType(SpdyFrameType::MAX_FRAME_TYPE);
+  switch (static_cast<SpdyFrameType>(frame_type_field)) {
+    case SpdyFrameType::DATA:
+      return true;
+    case SpdyFrameType::HEADERS:
+      return true;
+    case SpdyFrameType::PRIORITY:
+      return true;
+    case SpdyFrameType::RST_STREAM:
+      return true;
+    case SpdyFrameType::SETTINGS:
+      return true;
+    case SpdyFrameType::PUSH_PROMISE:
+      return true;
+    case SpdyFrameType::PING:
+      return true;
+    case SpdyFrameType::GOAWAY:
+      return true;
+    case SpdyFrameType::WINDOW_UPDATE:
+      return true;
+    case SpdyFrameType::CONTINUATION:
+      return true;
+    case SpdyFrameType::ALTSVC:
+      return true;
+  }
+  return false;
 }
 
 SpdyFrameType ParseFrameType(uint8_t frame_type_field) {
diff --git a/spdy/core/spdy_protocol.h b/spdy/core/spdy_protocol.h
index 175db51..39b3daa 100644
--- a/spdy/core/spdy_protocol.h
+++ b/spdy/core/spdy_protocol.h
@@ -99,7 +99,6 @@
   CONTINUATION = 0x09,
   // ALTSVC is a public extension.
   ALTSVC = 0x0a,
-  MAX_FRAME_TYPE = ALTSVC,
 };
 
 // Flags on data packets.