In quic, use transport params to announce the support of handshake_done frame. protected by gfe2_reloadable_flag_quic_support_handshake_done_in_t050.

PiperOrigin-RevId: 316678561
Change-Id: Ice2b7d65a0422bdab383e84ed75c7edc31488a53
diff --git a/quic/core/crypto/transport_parameters.cc b/quic/core/crypto/transport_parameters.cc
index f6d03b5..330a14d 100644
--- a/quic/core/crypto/transport_parameters.cc
+++ b/quic/core/crypto/transport_parameters.cc
@@ -56,6 +56,7 @@
   kInitialRoundTripTime = 0x3127,
   kGoogleConnectionOptions = 0x3128,
   kGoogleUserAgentId = 0x3129,
+  kGoogleSupportHandshakeDone = 0x312A,  // Only used in T050.
   kGoogleQuicParam = 18257,  // Used for non-standard Google-specific params.
   kGoogleQuicVersion =
       18258,  // Used to transmit version and supported_versions.
@@ -120,6 +121,8 @@
       return "google_connection_options";
     case TransportParameters::kGoogleUserAgentId:
       return "user_agent_id";
+    case TransportParameters::kGoogleSupportHandshakeDone:
+      return "support_handshake_done";
     case TransportParameters::kGoogleQuicParam:
       return "google";
     case TransportParameters::kGoogleQuicVersion:
@@ -152,6 +155,7 @@
     case TransportParameters::kInitialRoundTripTime:
     case TransportParameters::kGoogleConnectionOptions:
     case TransportParameters::kGoogleUserAgentId:
+    case TransportParameters::kGoogleSupportHandshakeDone:
     case TransportParameters::kGoogleQuicParam:
     case TransportParameters::kGoogleQuicVersion:
       return true;
@@ -465,6 +469,9 @@
     rv += " " + TransportParameterIdToString(kGoogleUserAgentId) + " \"" +
           user_agent_id.value() + "\"";
   }
+  if (support_handshake_done) {
+    rv += " " + TransportParameterIdToString(kGoogleSupportHandshakeDone);
+  }
   if (google_quic_params) {
     rv += " " + TransportParameterIdToString(kGoogleQuicParam);
   }
@@ -512,7 +519,8 @@
                                  kMinActiveConnectionIdLimitTransportParam,
                                  kVarInt62MaxValue),
       max_datagram_frame_size(kMaxDatagramFrameSize),
-      initial_round_trip_time_us(kInitialRoundTripTime)
+      initial_round_trip_time_us(kInitialRoundTripTime),
+      support_handshake_done(false)
 // Important note: any new transport parameters must be added
 // to TransportParameters::AreValid, SerializeTransportParameters and
 // ParseTransportParameters, TransportParameters's custom copy constructor, the
@@ -546,6 +554,7 @@
       initial_round_trip_time_us(other.initial_round_trip_time_us),
       google_connection_options(other.google_connection_options),
       user_agent_id(other.user_agent_id),
+      support_handshake_done(other.support_handshake_done),
       custom_parameters(other.custom_parameters) {
   if (other.preferred_address) {
     preferred_address = std::make_unique<TransportParameters::PreferredAddress>(
@@ -589,6 +598,7 @@
             rhs.initial_round_trip_time_us.value() &&
         google_connection_options == rhs.google_connection_options &&
         user_agent_id == rhs.user_agent_id &&
+        support_handshake_done == rhs.support_handshake_done &&
         custom_parameters == rhs.custom_parameters)) {
     return false;
   }
@@ -748,6 +758,7 @@
       kIntegerParameterLength +           // initial_round_trip_time_us
       kTypeAndValueLength +               // google_connection_options
       kTypeAndValueLength +               // user_agent_id
+      kTypeAndValueLength +               // support_handshake_done
       kTypeAndValueLength +               // google
       kTypeAndValueLength +               // google-version
       kGreaseParameterLength;             // GREASE
@@ -969,6 +980,17 @@
     }
   }
 
+  // Google-specific support handshake done.
+  if (in.support_handshake_done) {
+    if (!WriteTransportParameterId(
+            &writer, TransportParameters::kGoogleSupportHandshakeDone,
+            version) ||
+        !WriteTransportParameterLength(&writer, /*length=*/0, version)) {
+      QUIC_BUG << "Failed to write support_handshake_done for " << in;
+      return false;
+    }
+  }
+
   // Google-specific non-standard parameter.
   if (in.google_quic_params) {
     const QuicData& serialized_google_quic_params =
@@ -1330,6 +1352,13 @@
         }
         out->user_agent_id = std::string(value_reader.ReadRemainingPayload());
         break;
+      case TransportParameters::kGoogleSupportHandshakeDone:
+        if (out->support_handshake_done) {
+          *error_details = "Received a second support_handshake_done";
+          return false;
+        }
+        out->support_handshake_done = true;
+        break;
       case TransportParameters::kGoogleQuicParam: {
         if (out->google_quic_params) {
           *error_details = "Received a second Google parameter";
diff --git a/quic/core/crypto/transport_parameters.h b/quic/core/crypto/transport_parameters.h
index 8baeb21..093bb09 100644
--- a/quic/core/crypto/transport_parameters.h
+++ b/quic/core/crypto/transport_parameters.h
@@ -202,6 +202,9 @@
   // Google-specific user agent identifier.
   quiche::QuicheOptional<std::string> user_agent_id;
 
+  // Google-specific handshake done support. This is only used for T050.
+  bool support_handshake_done;
+
   // Transport parameters used by Google QUIC but not IETF QUIC. This is
   // serialized into a TransportParameter struct with a TransportParameterId of
   // kGoogleQuicParamId.
diff --git a/quic/core/crypto/transport_parameters_test.cc b/quic/core/crypto/transport_parameters_test.cc
index 8267c8b..3991953 100644
--- a/quic/core/crypto/transport_parameters_test.cc
+++ b/quic/core/crypto/transport_parameters_test.cc
@@ -44,6 +44,7 @@
 const uint8_t kFakePreferredStatelessResetTokenData[16] = {
     0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
     0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F};
+const bool kFakeSupportHandshakeDone = true;
 
 const auto kCustomParameter1 =
     static_cast<TransportParameters::TransportParameterId>(0xffcd);
@@ -281,6 +282,7 @@
   orig_params.initial_round_trip_time_us.set_value(kFakeInitialRoundTripTime);
   orig_params.google_connection_options = CreateFakeGoogleConnectionOptions();
   orig_params.user_agent_id = CreateFakeUserAgentId();
+  orig_params.support_handshake_done = kFakeSupportHandshakeDone;
   orig_params.custom_parameters[kCustomParameter1] = kCustomParameter1Value;
   orig_params.custom_parameters[kCustomParameter2] = kCustomParameter2Value;
 
@@ -313,6 +315,7 @@
   orig_params.initial_round_trip_time_us.set_value(kFakeInitialRoundTripTime);
   orig_params.google_connection_options = CreateFakeGoogleConnectionOptions();
   orig_params.user_agent_id = CreateFakeUserAgentId();
+  orig_params.support_handshake_done = kFakeSupportHandshakeDone;
   orig_params.custom_parameters[kCustomParameter1] = kCustomParameter1Value;
   orig_params.custom_parameters[kCustomParameter2] = kCustomParameter2Value;
 
@@ -487,7 +490,7 @@
 TEST_P(TransportParametersTest, ParseClientParams) {
   // clang-format off
   const uint8_t kClientParamsOld[] = {
-      0x00, 0x76,              // length of the parameters array that follows
+      0x00, 0x7A,              // length of the parameters array that follows
       // max_idle_timeout
       0x00, 0x01,  // parameter id
       0x00, 0x02,  // length
@@ -553,6 +556,9 @@
       0x31, 0x29,  // parameter id
       0x00, 0x08,  // length
       'F', 'a', 'k', 'e', 'U', 'A', 'I', 'D',  // value
+      // support_handshake_done
+      0x31, 0x2A,  // parameter id
+      0x00, 0x00,  // value
       // Google version extension
       0x47, 0x52,  // parameter id
       0x00, 0x04,  // length
@@ -624,6 +630,9 @@
       0x71, 0x29,  // parameter id
       0x08,  // length
       'F', 'a', 'k', 'e', 'U', 'A', 'I', 'D',  // value
+      // support_handshake_done
+      0x71, 0x2A,  // parameter id
+      0x00,  // length
       // Google version extension
       0x80, 0x00, 0x47, 0x52,  // parameter id
       0x04,  // length
@@ -679,6 +688,7 @@
             new_params.google_connection_options.value());
   ASSERT_TRUE(new_params.user_agent_id.has_value());
   EXPECT_EQ(CreateFakeUserAgentId(), new_params.user_agent_id.value());
+  EXPECT_TRUE(new_params.support_handshake_done);
 }
 
 TEST_P(TransportParametersTest,
@@ -846,7 +856,7 @@
 TEST_P(TransportParametersTest, ParseServerParams) {
   // clang-format off
   const uint8_t kServerParamsOld[] = {
-      0x00, 0xcf,  // length of parameters array that follows
+      0x00, 0xd3,  // length of parameters array that follows
       // original_destination_connection_id
       0x00, 0x00,  // parameter id
       0x00, 0x08,  // length
@@ -929,6 +939,9 @@
       'A', 'L', 'P', 'N',  // value
       'E', 'F', 'G', 0x00,
       'H', 'I', 'J', 0xff,
+      // support_handshake_done
+      0x31, 0x2A,  // parameter id
+      0x00, 0x00,  // value
       // Google version extension
       0x47, 0x52,  // parameter id
       0x00, 0x0d,  // length
@@ -1020,6 +1033,9 @@
       'A', 'L', 'P', 'N',  // value
       'E', 'F', 'G', 0x00,
       'H', 'I', 'J', 0xff,
+      // support_handshake_done
+      0x71, 0x2A,  // parameter id
+      0x00,  // length
       // Google version extension
       0x80, 0x00, 0x47, 0x52,  // parameter id
       0x0d,  // length
@@ -1090,6 +1106,7 @@
   EXPECT_EQ(CreateFakeGoogleConnectionOptions(),
             new_params.google_connection_options.value());
   EXPECT_FALSE(new_params.user_agent_id.has_value());
+  EXPECT_TRUE(new_params.support_handshake_done);
 }
 
 TEST_P(TransportParametersTest, ParseServerParametersRepeated) {
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index 03c83d2..abe9399 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -201,6 +201,8 @@
     SetQuicReloadableFlag(quic_donot_change_queued_ack, true);
     SetQuicReloadableFlag(quic_fix_last_inflight_packets_sent_time, true);
     SetQuicReloadableFlag(quic_fix_server_pto_timeout, true);
+
+    SetQuicReloadableFlag(quic_support_handshake_done_in_t050, true);
   }
 
   ~EndToEndTest() override { QuicRecyclePort(server_address_.port()); }
@@ -579,7 +581,7 @@
 
 TEST_P(EndToEndTest, HandshakeConfirmed) {
   ASSERT_TRUE(Initialize());
-  if (!version_.HasHandshakeDone()) {
+  if (!version_.UsesTls()) {
     return;
   }
   EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
@@ -1146,9 +1148,6 @@
   // brutal.
   SetPacketLossPercentage(5);
   ASSERT_TRUE(Initialize());
-  if (version_.UsesTls() && !version_.HasHandshakeDone()) {
-    return;
-  }
   EXPECT_TRUE(client_->client()->WaitForHandshakeConfirmed());
   SetPacketLossPercentage(30);
 
@@ -1168,9 +1167,6 @@
 // Regression test for b/80090281.
 TEST_P(EndToEndTest, LargePostWithPacketLossAndAlwaysBundleWindowUpdates) {
   ASSERT_TRUE(Initialize());
-  if (version_.UsesTls() && !version_.HasHandshakeDone()) {
-    return;
-  }
   EXPECT_TRUE(client_->client()->WaitForHandshakeConfirmed());
   server_thread_->WaitForCryptoHandshakeConfirmed();
 
@@ -1202,9 +1198,6 @@
   // b/10126687 is fixed, losing handshake packets is pretty brutal.
   SetPacketLossPercentage(5);
   ASSERT_TRUE(Initialize());
-  if (version_.UsesTls() && !version_.HasHandshakeDone()) {
-    return;
-  }
   EXPECT_TRUE(client_->client()->WaitForHandshakeConfirmed());
   SetPacketLossPercentage(10);
   client_writer_->set_fake_blocked_socket_percentage(10);
@@ -1223,9 +1216,6 @@
 
 TEST_P(EndToEndTest, LargePostNoPacketLossWithDelayAndReordering) {
   ASSERT_TRUE(Initialize());
-  if (version_.UsesTls() && !version_.HasHandshakeDone()) {
-    return;
-  }
   EXPECT_TRUE(client_->client()->WaitForHandshakeConfirmed());
   // Both of these must be called when the writer is not actively used.
   SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2));
@@ -2413,9 +2403,6 @@
   // demonstrates that retransmissions do not break this functionality.
   SetPacketLossPercentage(5);
   ASSERT_TRUE(Initialize());
-  if (version_.UsesTls() && !version_.HasHandshakeDone()) {
-    return;
-  }
   // Wait for the server SHLO before upping the packet loss.
   EXPECT_TRUE(client_->client()->WaitForHandshakeConfirmed());
   SetPacketLossPercentage(30);
@@ -3789,9 +3776,6 @@
 
 TEST_P(EndToEndTest, ResetStreamOnTtlExpires) {
   ASSERT_TRUE(Initialize());
-  if (version_.UsesTls() && !version_.HasHandshakeDone()) {
-    return;
-  }
   EXPECT_TRUE(client_->client()->WaitForHandshakeConfirmed());
   SetPacketLossPercentage(30);
 
diff --git a/quic/core/quic_config.cc b/quic/core/quic_config.cc
index fa06bcb..68638e4 100644
--- a/quic/core/quic_config.cc
+++ b/quic/core/quic_config.cc
@@ -447,6 +447,7 @@
       initial_stream_flow_control_window_bytes_(kSFCW, PRESENCE_OPTIONAL),
       initial_session_flow_control_window_bytes_(kCFCW, PRESENCE_OPTIONAL),
       connection_migration_disabled_(kNCMR, PRESENCE_OPTIONAL),
+      support_handshake_done_(0, PRESENCE_OPTIONAL),
       alternate_server_address_ipv6_(kASAD, PRESENCE_OPTIONAL),
       alternate_server_address_ipv4_(kASAD, PRESENCE_OPTIONAL),
       stateless_reset_token_(kSRST, PRESENCE_OPTIONAL),
@@ -832,6 +833,19 @@
   return connection_migration_disabled_.HasReceivedValue();
 }
 
+void QuicConfig::SetSupportHandshakeDone() {
+  support_handshake_done_.SetSendValue(1);
+}
+
+bool QuicConfig::HandshakeDoneSupported() const {
+  return support_handshake_done_.HasSendValue() &&
+         support_handshake_done_.GetSendValue() > 0;
+}
+
+bool QuicConfig::PeerSupportsHandshakeDone() const {
+  return support_handshake_done_.HasReceivedValue();
+}
+
 void QuicConfig::SetIPv6AlternateServerAddressToSend(
     const QuicSocketAddress& alternate_server_address_ipv6) {
   if (!alternate_server_address_ipv6.host().IsIPv6()) {
@@ -1154,6 +1168,7 @@
   params->disable_active_migration =
       connection_migration_disabled_.HasSendValue() &&
       connection_migration_disabled_.GetSendValue() != 0;
+  params->support_handshake_done = HandshakeDoneSupported();
 
   if (alternate_server_address_ipv6_.HasSendValue() ||
       alternate_server_address_ipv4_.HasSendValue()) {
@@ -1304,6 +1319,9 @@
   if (params.disable_active_migration) {
     connection_migration_disabled_.SetReceivedValue(1u);
   }
+  if (params.support_handshake_done) {
+    support_handshake_done_.SetReceivedValue(1u);
+  }
 
   active_connection_id_limit_.SetReceivedValue(
       params.active_connection_id_limit.value());
diff --git a/quic/core/quic_config.h b/quic/core/quic_config.h
index 4f302b6..6f041b8 100644
--- a/quic/core/quic_config.h
+++ b/quic/core/quic_config.h
@@ -382,6 +382,11 @@
   void SetDisableConnectionMigration();
   bool DisableConnectionMigration() const;
 
+  // Support handshake done.
+  void SetSupportHandshakeDone();
+  bool HandshakeDoneSupported() const;
+  bool PeerSupportsHandshakeDone() const;
+
   // IPv6 alternate server address.
   void SetIPv6AlternateServerAddressToSend(
       const QuicSocketAddress& alternate_server_address_ipv6);
@@ -566,6 +571,10 @@
   // Uses the disable_active_migration transport parameter in IETF QUIC.
   QuicFixedUint32 connection_migration_disabled_;
 
+  // Whether handshake done is supported. Only used in T050.
+  // Uses the support_handshake_done transport parameter in IETF QUIC.
+  QuicFixedUint32 support_handshake_done_;
+
   // Alternate server addresses the client could connect to.
   // Uses the preferred_address transport parameter in IETF QUIC.
   // Note that when QUIC_CRYPTO is in use, only one of the addresses is sent.
diff --git a/quic/core/quic_config_test.cc b/quic/core/quic_config_test.cc
index 4aa9619..16c7c56 100644
--- a/quic/core/quic_config_test.cc
+++ b/quic/core/quic_config_test.cc
@@ -561,6 +561,7 @@
             config_.ReceivedMaxBidirectionalStreams());
 
   EXPECT_FALSE(config_.DisableConnectionMigration());
+  EXPECT_FALSE(config_.PeerSupportsHandshakeDone());
 
   // The following config shouldn't be processed because of resumption.
   EXPECT_FALSE(config_.HasReceivedStatelessResetToken());
@@ -584,6 +585,7 @@
   params.initial_max_streams_bidi.set_value(2 *
                                             kDefaultMaxStreamsPerConnection);
   params.disable_active_migration = true;
+  params.support_handshake_done = true;
 
   EXPECT_THAT(config_.ProcessTransportParameters(
                   params, SERVER, /* is_resumption = */ false, &error_details),
@@ -618,6 +620,7 @@
             config_.ReceivedMaxBidirectionalStreams());
 
   EXPECT_TRUE(config_.DisableConnectionMigration());
+  EXPECT_TRUE(config_.PeerSupportsHandshakeDone());
 
   ASSERT_TRUE(config_.HasReceivedStatelessResetToken());
   ASSERT_TRUE(config_.HasReceivedMaxAckDelayMs());
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 8e85af9..b22dc96 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -320,7 +320,8 @@
       idle_network_detector_(this,
                              clock_->ApproximateNow(),
                              &arena_,
-                             alarm_factory_) {
+                             alarm_factory_),
+      support_handshake_done_(version().HasHandshakeDone()) {
   QUIC_DLOG(INFO) << ENDPOINT << "Created connection with server connection ID "
                   << server_connection_id
                   << " and version: " << ParsedQuicVersionToString(version());
@@ -539,6 +540,9 @@
     SetNetworkTimeouts(config.max_time_before_crypto_handshake(),
                        config.max_idle_time_before_crypto_handshake());
   }
+  if (config.HandshakeDoneSupported()) {
+    support_handshake_done_ = true;
+  }
 
   sent_packet_manager_.SetFromConfig(config);
   if (config.HasReceivedBytesForConnectionId() &&
@@ -1586,7 +1590,13 @@
 }
 
 bool QuicConnection::OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) {
-  DCHECK(connected_ && VersionHasIetfQuicFrames(transport_version()));
+  DCHECK(connected_);
+  if (!support_handshake_done_) {
+    CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION,
+                    "Handshake done frame is unsupported",
+                    ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+    return false;
+  }
 
   if (perspective_ == Perspective::IS_SERVER) {
     CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION,
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 8e3608b..7339b57 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -1670,6 +1670,9 @@
 
   bool blackhole_detection_disabled_ = false;
 
+  // True if this connection supports handshake done frame.
+  bool support_handshake_done_;
+
   const bool use_idle_network_detector_ =
       GetQuicReloadableFlag(quic_use_idle_network_detector);
 
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc
index 1cba589..726f1e5 100644
--- a/quic/core/quic_framer.cc
+++ b/quic/core/quic_framer.cc
@@ -1013,6 +1013,9 @@
           return 0;
         }
         break;
+      case HANDSHAKE_DONE_FRAME:
+        // HANDSHAKE_DONE has no payload.
+        break;
       default:
         RaiseError(QUIC_INVALID_FRAME_DATA);
         QUIC_BUG << "QUIC_INVALID_FRAME_DATA";
@@ -2986,6 +2989,19 @@
         }
         break;
       }
+      case HANDSHAKE_DONE_FRAME: {
+        // HANDSHAKE_DONE has no payload.
+        QuicHandshakeDoneFrame handshake_done_frame;
+        QUIC_DVLOG(2) << ENDPOINT << "Processing handshake done frame "
+                      << handshake_done_frame;
+        if (!visitor_->OnHandshakeDoneFrame(handshake_done_frame)) {
+          QUIC_DVLOG(1) << ENDPOINT
+                        << "Visitor asked to stop further processing.";
+          // Returning true since there was no parsing error.
+          return true;
+        }
+        break;
+      }
 
       default:
         set_detailed_error("Illegal frame type.");
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index 763b291..ad7fce3 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -118,7 +118,11 @@
   connection_->SetSessionNotifier(this);
   connection_->SetDataProducer(this);
   connection_->SetFromConfig(config_);
-
+  if (GetQuicReloadableFlag(quic_support_handshake_done_in_t050) &&
+      version().UsesTls() && !version().HasHandshakeDone() &&
+      perspective_ == Perspective::IS_CLIENT) {
+    config_.SetSupportHandshakeDone();
+  }
   // On the server side, version negotiation has been done by the dispatcher,
   // and the server session is created with the right version.
   if (perspective() == Perspective::IS_SERVER) {
@@ -1545,8 +1549,13 @@
       << ENDPOINT << "Handshake completes without cipher suite negotiation.";
   QUIC_BUG_IF(!config_.negotiated())
       << ENDPOINT << "Handshake completes without parameter negotiation.";
-  if (connection()->version().HasHandshakeDone() &&
+  if ((connection()->version().HasHandshakeDone() ||
+       (GetQuicReloadableFlag(quic_support_handshake_done_in_t050) &&
+        config_.PeerSupportsHandshakeDone())) &&
       perspective_ == Perspective::IS_SERVER) {
+    if (!connection()->version().HasHandshakeDone()) {
+      QUIC_RELOADABLE_FLAG_COUNT(quic_support_handshake_done_in_t050);
+    }
     // Server sends HANDSHAKE_DONE to signal confirmation of the handshake
     // to the client.
     control_frame_manager_.WriteOrBufferHandshakeDone();
diff --git a/quic/core/quic_types.h b/quic/core/quic_types.h
index 753f86e..39e1a87 100644
--- a/quic/core/quic_types.h
+++ b/quic/core/quic_types.h
@@ -227,6 +227,8 @@
   STOP_WAITING_FRAME = 6,
   PING_FRAME = 7,
   CRYPTO_FRAME = 8,
+  // TODO(b/157935330): stop hard coding this when deprecate T050.
+  HANDSHAKE_DONE_FRAME = 9,
 
   // STREAM and ACK frames are special frames. They are encoded differently on
   // the wire and their values do not need to be stable.
@@ -248,7 +250,6 @@
   MESSAGE_FRAME,
   NEW_TOKEN_FRAME,
   RETIRE_CONNECTION_ID_FRAME,
-  HANDSHAKE_DONE_FRAME,
 
   NUM_FRAME_TYPES
 };
diff --git a/quic/test_tools/quic_config_peer.cc b/quic/test_tools/quic_config_peer.cc
index b380642..52325d1 100644
--- a/quic/test_tools/quic_config_peer.cc
+++ b/quic/test_tools/quic_config_peer.cc
@@ -132,5 +132,10 @@
   config->max_datagram_frame_size_.SetReceivedValue(max_datagram_frame_size);
 }
 
+// static
+void QuicConfigPeer::DisableSupportHandshakeDone(QuicConfig* config) {
+  config->support_handshake_done_.SetSendValue(0);
+}
+
 }  // namespace test
 }  // namespace quic
diff --git a/quic/test_tools/quic_config_peer.h b/quic/test_tools/quic_config_peer.h
index a351907..109bd64 100644
--- a/quic/test_tools/quic_config_peer.h
+++ b/quic/test_tools/quic_config_peer.h
@@ -76,6 +76,7 @@
 
   static void SetReceivedMaxDatagramFrameSize(QuicConfig* config,
                                               uint64_t max_datagram_frame_size);
+  static void DisableSupportHandshakeDone(QuicConfig* config);
 };
 
 }  // namespace test