Add transport parameter 'discard' to QUIC and switch connection option `CHP1` and `CHP2` to use it.

This parameter is meant to be discarded by the receiver. It is mainly used to make the ClientHello larger for testing.

PiperOrigin-RevId: 691180591
diff --git a/quiche/quic/core/crypto/transport_parameters.cc b/quiche/quic/core/crypto/transport_parameters.cc
index 0456134..f03e67e 100644
--- a/quiche/quic/core/crypto/transport_parameters.cc
+++ b/quiche/quic/core/crypto/transport_parameters.cc
@@ -57,6 +57,9 @@
 
   kMaxDatagramFrameSize = 0x20,
 
+  // https://github.com/quicwg/base-drafts/wiki/Quantum-Readiness-test
+  kDiscard = 0x173E,
+
   kGoogleHandshakeMessage = 0x26ab,
 
   kInitialRoundTripTime = 0x3127,
@@ -71,8 +74,9 @@
 
   kMinAckDelay = 0xDE1A,           // draft-iyengar-quic-delayed-ack.
   kVersionInformation = 0xFF73DB,  // draft-ietf-quic-version-negotiation.
-  kReliableStreamReset = 0x17F7586D2CB571,
+
   // draft-ietf-quic-reliable-stream-reset.
+  kReliableStreamReset = 0x17F7586D2CB571,
 };
 
 namespace {
@@ -130,6 +134,8 @@
       return "retry_source_connection_id";
     case TransportParameters::kMaxDatagramFrameSize:
       return "max_datagram_frame_size";
+    case TransportParameters::kDiscard:
+      return "discard";
     case TransportParameters::kGoogleHandshakeMessage:
       return "google_handshake_message";
     case TransportParameters::kInitialRoundTripTime:
@@ -169,6 +175,7 @@
     case TransportParameters::kInitialSourceConnectionId:
     case TransportParameters::kRetrySourceConnectionId:
     case TransportParameters::kMaxDatagramFrameSize:
+    case TransportParameters::kDiscard:
     case TransportParameters::kGoogleHandshakeMessage:
     case TransportParameters::kInitialRoundTripTime:
     case TransportParameters::kGoogleConnectionOptions:
@@ -439,6 +446,10 @@
           retry_source_connection_id->ToString();
   }
   rv += max_datagram_frame_size.ToString(/*for_use_in_list=*/true);
+  if (discard_length >= 0) {
+    absl::StrAppend(&rv, " ", TransportParameterIdToString(kDiscard),
+                    " length: ", discard_length);
+  }
   if (google_handshake_message.has_value()) {
     absl::StrAppend(&rv, " ",
                     TransportParameterIdToString(kGoogleHandshakeMessage),
@@ -532,6 +543,7 @@
       max_datagram_frame_size(other.max_datagram_frame_size),
       reliable_stream_reset(other.reliable_stream_reset),
       initial_round_trip_time_us(other.initial_round_trip_time_us),
+      discard_length(other.discard_length),
       google_handshake_message(other.google_handshake_message),
       google_connection_options(other.google_connection_options),
       custom_parameters(other.custom_parameters) {
@@ -574,6 +586,7 @@
         reliable_stream_reset == rhs.reliable_stream_reset &&
         initial_round_trip_time_us.value() ==
             rhs.initial_round_trip_time_us.value() &&
+        discard_length == rhs.discard_length &&
         google_handshake_message == rhs.google_handshake_message &&
         google_connection_options == rhs.google_connection_options &&
         custom_parameters == rhs.custom_parameters)) {
@@ -762,6 +775,7 @@
       kIntegerParameterLength +           // max_datagram_frame_size
       kTypeAndValueLength +               // reliable_stream_reset
       kIntegerParameterLength +           // initial_round_trip_time_us
+      kTypeAndValueLength +               // discard
       kTypeAndValueLength +               // google_handshake_message
       kTypeAndValueLength +               // google_connection_options
       kTypeAndValueLength;                // google-version
@@ -783,6 +797,7 @@
       TransportParameters::kActiveConnectionIdLimit,
       TransportParameters::kMaxDatagramFrameSize,
       TransportParameters::kReliableStreamReset,
+      TransportParameters::kDiscard,
       TransportParameters::kGoogleHandshakeMessage,
       TransportParameters::kInitialRoundTripTime,
       TransportParameters::kDisableActiveMigration,
@@ -816,6 +831,10 @@
         (in.version_information->other_versions.size() + 1) *
             sizeof(QuicVersionLabel);
   }
+  // discard.
+  if (in.discard_length >= 0) {
+    max_transport_param_length += in.discard_length;
+  }
   // google_handshake_message.
   if (in.google_handshake_message.has_value()) {
     max_transport_param_length += in.google_handshake_message->length();
@@ -1001,6 +1020,19 @@
           return false;
         }
       } break;
+      // discard
+      case TransportParameters::kDiscard: {
+        if (in.discard_length >= 0) {
+          std::string discard_data(in.discard_length, '\0');
+          if (!writer.WriteVarInt62(TransportParameters::kDiscard) ||
+              !writer.WriteStringPieceVarInt62(discard_data)) {
+            QUIC_BUG(Failed to write discard_data)
+                << "Failed to write discard data of length: "
+                << in.discard_length << " for " << in;
+            return false;
+          }
+        }
+      } break;
       // google_handshake_message
       case TransportParameters::kGoogleHandshakeMessage: {
         if (in.google_handshake_message.has_value()) {
@@ -1250,7 +1282,7 @@
                   << " bytes";
 
   return true;
-}
+}  // NOLINT(readability/fn_size)
 
 bool ParseTransportParameters(ParsedQuicVersion version,
                               Perspective perspective, const uint8_t* in,
@@ -1451,6 +1483,9 @@
         parse_success =
             out->max_datagram_frame_size.Read(&value_reader, error_details);
         break;
+      case TransportParameters::kDiscard:
+        out->discard_length = value_reader.ReadRemainingPayload().length();
+        break;
       case TransportParameters::kGoogleHandshakeMessage:
         if (out->google_handshake_message.has_value()) {
           *error_details = "Received a second google_handshake_message";
diff --git a/quiche/quic/core/crypto/transport_parameters.h b/quiche/quic/core/crypto/transport_parameters.h
index 9255793..5efba82 100644
--- a/quiche/quic/core/crypto/transport_parameters.h
+++ b/quiche/quic/core/crypto/transport_parameters.h
@@ -5,6 +5,7 @@
 #ifndef QUICHE_QUIC_CORE_CRYPTO_TRANSPORT_PARAMETERS_H_
 #define QUICHE_QUIC_CORE_CRYPTO_TRANSPORT_PARAMETERS_H_
 
+#include <cstdint>
 #include <memory>
 #include <optional>
 #include <vector>
@@ -255,6 +256,10 @@
   // initial round-trip time in microseconds.
   IntegerParameter initial_round_trip_time_us;
 
+  // Data length for TransportParameterId::kDiscard. Negative values means the
+  // parameter is not set.
+  int32_t discard_length = -1;
+
   // Google internal handshake message.
   std::optional<std::string> google_handshake_message;
 
diff --git a/quiche/quic/core/crypto/transport_parameters_test.cc b/quiche/quic/core/crypto/transport_parameters_test.cc
index 4fb1fed..8f3c6c9 100644
--- a/quiche/quic/core/crypto/transport_parameters_test.cc
+++ b/quiche/quic/core/crypto/transport_parameters_test.cc
@@ -296,6 +296,7 @@
       CreateFakeInitialSourceConnectionId();
   orig_params.retry_source_connection_id = CreateFakeRetrySourceConnectionId();
   orig_params.initial_round_trip_time_us.set_value(kFakeInitialRoundTripTime);
+  orig_params.discard_length = 2000;
   std::string google_handshake_message;
   ASSERT_TRUE(absl::HexStringToBytes(kFakeGoogleHandshakeMessage,
                                      &google_handshake_message));
@@ -335,6 +336,7 @@
   orig_params.initial_source_connection_id =
       CreateFakeInitialSourceConnectionId();
   orig_params.initial_round_trip_time_us.set_value(kFakeInitialRoundTripTime);
+  orig_params.discard_length = 2000;
   std::string google_handshake_message;
   ASSERT_TRUE(absl::HexStringToBytes(kFakeGoogleHandshakeMessage,
                                      &google_handshake_message));
@@ -573,6 +575,11 @@
       0x0f,  // parameter id
       0x08,  // length
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x45,
+      // discard
+      0x57, 0x3e,  // parameter id
+      0x10,  // length
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       // google_handshake_message
       0x66, 0xab,  // parameter id
       0x24,  // length
@@ -652,6 +659,7 @@
   ASSERT_TRUE(new_params.google_connection_options.has_value());
   EXPECT_EQ(CreateFakeGoogleConnectionOptions(),
             new_params.google_connection_options.value());
+  EXPECT_EQ(16, new_params.discard_length);
   std::string expected_google_handshake_message;
   ASSERT_TRUE(absl::HexStringToBytes(kFakeGoogleHandshakeMessage,
                                      &expected_google_handshake_message));
diff --git a/quiche/quic/core/quic_config.cc b/quiche/quic/core/quic_config.cc
index 6e3f8d4..308d2d9 100644
--- a/quiche/quic/core/quic_config.cc
+++ b/quiche/quic/core/quic_config.cc
@@ -501,6 +501,10 @@
   return received_google_handshake_message_;
 }
 
+void QuicConfig::SetDiscardLengthToSend(int32_t discard_length) {
+  discard_length_to_send_ = discard_length;
+}
+
 bool QuicConfig::HasReceivedConnectionOptions() const {
   return connection_options_.HasReceivedValues();
 }
@@ -1279,6 +1283,8 @@
     params->google_handshake_message = google_handshake_message_to_send_;
   }
 
+  params->discard_length = discard_length_to_send_;
+
   params->reliable_stream_reset = reliable_stream_reset_;
 
   params->custom_parameters = custom_transport_parameters_to_send_;
@@ -1416,6 +1422,8 @@
 
   received_custom_transport_parameters_ = params.custom_parameters;
 
+  discard_length_received_ = params.discard_length;
+
   if (reliable_stream_reset_) {
     reliable_stream_reset_ = params.reliable_stream_reset;
   }
diff --git a/quiche/quic/core/quic_config.h b/quiche/quic/core/quic_config.h
index dd72974..cfa3628 100644
--- a/quiche/quic/core/quic_config.h
+++ b/quiche/quic/core/quic_config.h
@@ -250,6 +250,13 @@
 
   bool HasReceivedConnectionOptions() const;
 
+  // Sets the data length to be sent for transport parameter 'discard'. The data
+  // to send in the transport parameter will be all zeros. Negative values means
+  // do not send.
+  void SetDiscardLengthToSend(int32_t discard_length);
+
+  int32_t GetDiscardLengthReceived() const { return discard_length_received_; }
+
   void SetGoogleHandshakeMessageToSend(std::string message);
 
   const std::optional<std::string>& GetReceivedGoogleHandshakeMessage() const;
@@ -707,6 +714,14 @@
   TransportParameters::ParameterMap custom_transport_parameters_to_send_;
   TransportParameters::ParameterMap received_custom_transport_parameters_;
 
+  // Length of the data to send in the 'discard' transport parameter. Negative
+  // values means do not send.
+  int32_t discard_length_to_send_ = -1;
+
+  // Length of the receive data in the 'discard' transport parameter. Negative
+  // values means 'discard' data not received.
+  int32_t discard_length_received_ = -1;
+
   // Google internal handshake message.
   std::optional<std::string> google_handshake_message_to_send_;
   std::optional<std::string> received_google_handshake_message_;
diff --git a/quiche/quic/core/quic_config_test.cc b/quiche/quic/core/quic_config_test.cc
index ea311e9..24a2ec1 100644
--- a/quiche/quic/core/quic_config_test.cc
+++ b/quiche/quic/core/quic_config_test.cc
@@ -4,6 +4,7 @@
 
 #include "quiche/quic/core/quic_config.h"
 
+#include <cstdint>
 #include <memory>
 #include <string>
 #include <utility>
@@ -467,6 +468,7 @@
     return;
   }
   const std::string kFakeGoogleHandshakeMessage = "Fake handshake message";
+  const int32_t kDiscardLength = 2000;
   config_.SetInitialMaxStreamDataBytesIncomingBidirectionalToSend(
       2 * kMinimumFlowControlSendWindow);
   config_.SetInitialMaxStreamDataBytesOutgoingBidirectionalToSend(
@@ -481,6 +483,7 @@
   config_.SetInitialSourceConnectionIdToSend(TestConnectionId(0x2222));
   config_.SetRetrySourceConnectionIdToSend(TestConnectionId(0x3333));
   config_.SetMinAckDelayMs(kDefaultMinAckDelayTimeMs);
+  config_.SetDiscardLengthToSend(kDiscardLength);
   config_.SetGoogleHandshakeMessageToSend(kFakeGoogleHandshakeMessage);
   config_.SetReliableStreamReset(true);
 
@@ -542,6 +545,7 @@
   EXPECT_EQ(*reinterpret_cast<StatelessResetToken*>(
                 &params.preferred_address->stateless_reset_token.front()),
             new_stateless_reset_token);
+  EXPECT_EQ(kDiscardLength, params.discard_length);
   EXPECT_EQ(kFakeGoogleHandshakeMessage, params.google_handshake_message);
 
   EXPECT_TRUE(params.reliable_stream_reset);
@@ -655,6 +659,7 @@
     return;
   }
   const std::string kFakeGoogleHandshakeMessage = "Fake handshake message";
+  const int32_t kDiscardLength = 2000;
   TransportParameters params;
 
   params.initial_max_stream_data_bidi_local.set_value(
@@ -674,6 +679,7 @@
   params.original_destination_connection_id = TestConnectionId(0x1111);
   params.initial_source_connection_id = TestConnectionId(0x2222);
   params.retry_source_connection_id = TestConnectionId(0x3333);
+  params.discard_length = kDiscardLength;
   params.google_handshake_message = kFakeGoogleHandshakeMessage;
 
   std::string error_details;
@@ -795,6 +801,7 @@
             TestConnectionId(0x3333));
   EXPECT_EQ(kFakeGoogleHandshakeMessage,
             config_.GetReceivedGoogleHandshakeMessage());
+  EXPECT_EQ(kDiscardLength, config_.GetDiscardLengthReceived());
 }
 
 TEST_P(QuicConfigTest, DisableMigrationTransportParameter) {
diff --git a/quiche/quic/core/quic_session.cc b/quiche/quic/core/quic_session.cc
index b83d773..829d7f1 100644
--- a/quiche/quic/core/quic_session.cc
+++ b/quiche/quic/core/quic_session.cc
@@ -174,11 +174,9 @@
   connection_->SetUnackedMapInitialCapacity();
   if (perspective_ == Perspective::IS_CLIENT) {
     if (config_.HasClientSentConnectionOption(kCHP1, perspective_)) {
-      config_.SetGoogleHandshakeMessageToSend(
-          std::string(kDefaultMaxPacketSize, '0'));
+      config_.SetDiscardLengthToSend(kDefaultMaxPacketSize);
     } else if (config_.HasClientSentConnectionOption(kCHP2, perspective_)) {
-      config_.SetGoogleHandshakeMessageToSend(
-          std::string(kDefaultMaxPacketSize * 2, '0'));
+      config_.SetDiscardLengthToSend(kDefaultMaxPacketSize * 2);
     }
   }
   connection_->SetFromConfig(config_);
@@ -1873,11 +1871,6 @@
       MaybeSendAddressToken();
     }
   }
-  if (perspective_ == Perspective::IS_CLIENT &&
-      (config_.HasClientSentConnectionOption(kCHP1, perspective_) ||
-       config_.HasClientSentConnectionOption(kCHP2, perspective_))) {
-    config_.ClearGoogleHandshakeMessage();
-  }
 }
 
 bool QuicSession::MaybeSendAddressToken() {
diff --git a/quiche/quic/tools/quic_client_interop_test_bin.cc b/quiche/quic/tools/quic_client_interop_test_bin.cc
index 0df5e68..71ee23f 100644
--- a/quiche/quic/tools/quic_client_interop_test_bin.cc
+++ b/quiche/quic/tools/quic_client_interop_test_bin.cc
@@ -213,11 +213,7 @@
   if (attempt_multi_packet_chlo) {
     // Make the ClientHello span multiple packets by adding a custom transport
     // parameter.
-    constexpr auto kCustomParameter =
-        static_cast<TransportParameters::TransportParameterId>(0x173E);
-    std::string custom_value(2000, '?');
-    config.custom_transport_parameters_to_send()[kCustomParameter] =
-        custom_value;
+    config.SetDiscardLengthToSend(2000);
   }
   std::unique_ptr<QuicEventLoop> event_loop =
       GetDefaultEventLoop()->Create(QuicDefaultClock::Get());
diff --git a/quiche/quic/tools/quic_toy_client.cc b/quiche/quic/tools/quic_toy_client.cc
index 79aafb7..1857025 100644
--- a/quiche/quic/tools/quic_toy_client.cc
+++ b/quiche/quic/tools/quic_toy_client.cc
@@ -312,13 +312,9 @@
         ParseQuicTagVector(client_connection_options_string));
   }
   if (quiche::GetQuicheCommandLineFlag(FLAGS_multi_packet_chlo)) {
-    // Make the ClientHello span multiple packets by adding a custom transport
-    // parameter.
-    constexpr auto kCustomParameter =
-        static_cast<TransportParameters::TransportParameterId>(0x173E);
-    std::string custom_value(2000, '?');
-    config.custom_transport_parameters_to_send()[kCustomParameter] =
-        custom_value;
+    // Make the ClientHello span multiple packets by adding a large 'discard'
+    // transport parameter.
+    config.SetDiscardLengthToSend(2000);
   }
   config.set_max_time_before_crypto_handshake(
       QuicTime::Delta::FromMilliseconds(quiche::GetQuicheCommandLineFlag(