Ignore user agent transport parameter

In gQUIC, we used to send the user agent string via the UAID tag on the CHLO. In IETF QUIC, the user agent is communicated using a regular HTTP header (and compressed using QPACK). While transitioning from gQUIC to IETF QUIC, we had added this custom Google-only transport parameter to facilitate migration. Now that we are getting production traffic from other browsers that do not send this transport parameter, we're confident that our infrastructure is correctly getting the user agent from the HTTP header and this transport parameter is therefore mostly redundant. We'll lose the ability to see the UAID before the first request but that's not much of a concern in a world where browsers are working towards freezing their user agent strings. Removing it also improves user privacy because it is sent in the clear and enables fingerprinting. Once this flag is deprecated, we will remove the transport parameter entirely in a follow-up CL.

Protected by FLAGS_quic_reloadable_flag_quic_ignore_user_agent_transport_parameter.

PiperOrigin-RevId: 402616442
diff --git a/quic/core/crypto/transport_parameters.cc b/quic/core/crypto/transport_parameters.cc
index 75d4323..dcd2fc9 100644
--- a/quic/core/crypto/transport_parameters.cc
+++ b/quic/core/crypto/transport_parameters.cc
@@ -156,11 +156,12 @@
     case TransportParameters::kMaxDatagramFrameSize:
     case TransportParameters::kInitialRoundTripTime:
     case TransportParameters::kGoogleConnectionOptions:
-    case TransportParameters::kGoogleUserAgentId:
     case TransportParameters::kGoogleKeyUpdateNotYetSupported:
     case TransportParameters::kGoogleQuicVersion:
     case TransportParameters::kMinAckDelay:
       return true;
+    case TransportParameters::kGoogleUserAgentId:
+      return !GetQuicReloadableFlag(quic_ignore_user_agent_transport_parameter);
   }
   return false;
 }
@@ -1327,6 +1328,22 @@
         }
       } break;
       case TransportParameters::kGoogleUserAgentId:
+        if (GetQuicReloadableFlag(quic_ignore_user_agent_transport_parameter)) {
+          QUIC_RELOADABLE_FLAG_COUNT(
+              quic_ignore_user_agent_transport_parameter);
+          // This is a copy of the default switch statement below.
+          // TODO(dschinazi) remove this case entirely when deprecating the
+          // quic_ignore_user_agent_transport_parameter flag.
+          if (out->custom_parameters.find(param_id) !=
+              out->custom_parameters.end()) {
+            *error_details = "Received a second unknown parameter" +
+                             TransportParameterIdToString(param_id);
+            return false;
+          }
+          out->custom_parameters[param_id] =
+              std::string(value_reader.ReadRemainingPayload());
+          break;
+        }
         if (out->user_agent_id.has_value()) {
           *error_details = "Received a second user_agent_id";
           return false;
diff --git a/quic/core/crypto/transport_parameters_test.cc b/quic/core/crypto/transport_parameters_test.cc
index 48af1c4..a758d81 100644
--- a/quic/core/crypto/transport_parameters_test.cc
+++ b/quic/core/crypto/transport_parameters_test.cc
@@ -282,7 +282,9 @@
       CreateFakeInitialSourceConnectionId();
   orig_params.initial_round_trip_time_us.set_value(kFakeInitialRoundTripTime);
   orig_params.google_connection_options = CreateFakeGoogleConnectionOptions();
-  orig_params.user_agent_id = CreateFakeUserAgentId();
+  if (!GetQuicReloadableFlag(quic_ignore_user_agent_transport_parameter)) {
+    orig_params.user_agent_id = CreateFakeUserAgentId();
+  }
   orig_params.key_update_not_yet_supported = kFakeKeyUpdateNotYetSupported;
   orig_params.custom_parameters[kCustomParameter1] = kCustomParameter1Value;
   orig_params.custom_parameters[kCustomParameter2] = kCustomParameter2Value;
@@ -581,8 +583,12 @@
   ASSERT_TRUE(new_params.google_connection_options.has_value());
   EXPECT_EQ(CreateFakeGoogleConnectionOptions(),
             new_params.google_connection_options.value());
-  ASSERT_TRUE(new_params.user_agent_id.has_value());
-  EXPECT_EQ(CreateFakeUserAgentId(), new_params.user_agent_id.value());
+  if (!GetQuicReloadableFlag(quic_ignore_user_agent_transport_parameter)) {
+    ASSERT_TRUE(new_params.user_agent_id.has_value());
+    EXPECT_EQ(CreateFakeUserAgentId(), new_params.user_agent_id.value());
+  } else {
+    EXPECT_FALSE(new_params.user_agent_id.has_value());
+  }
   EXPECT_TRUE(new_params.key_update_not_yet_supported);
 }
 
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index ba3c7a7..3b073af 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -547,8 +547,12 @@
           EXPECT_EQ(0u, server_stats.packets_lost);
         }
         EXPECT_EQ(0u, server_stats.packets_discarded);
-        EXPECT_EQ(server_session->user_agent_id().value_or("MissingUserAgent"),
-                  kTestUserAgentId);
+        if (!GetQuicReloadableFlag(
+                quic_ignore_user_agent_transport_parameter)) {
+          EXPECT_EQ(
+              server_session->user_agent_id().value_or("MissingUserAgent"),
+              kTestUserAgentId);
+        }
       } else {
         ADD_FAILURE() << "Missing server connection";
       }
@@ -5594,8 +5598,10 @@
   QuicConfig* server_config = nullptr;
   if (server_session != nullptr) {
     server_config = server_session->config();
-    EXPECT_EQ(server_session->user_agent_id().value_or("MissingUserAgent"),
-              kTestUserAgentId);
+    if (!GetQuicReloadableFlag(quic_ignore_user_agent_transport_parameter)) {
+      EXPECT_EQ(server_session->user_agent_id().value_or("MissingUserAgent"),
+                kTestUserAgentId);
+    }
   } else {
     ADD_FAILURE() << "Missing server session";
   }
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 994bbdd..b40bd6c 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -121,6 +121,8 @@
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_match_ietf_reset_code, false)
 // When the flag is true, exit STARTUP after the same number of loss events as PROBE_UP.
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_startup_probe_up_loss_events, true)
+// When true, QUIC server will ignore received user agent transport parameter and rely on getting that information from HTTP headers.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_ignore_user_agent_transport_parameter, false)
 // When true, QuicDispatcher will silently drop incoming packets whose UDP source port is on the blocklist.
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_blocked_ports, false)
 // When true, defaults to BBR congestion control instead of Cubic.