Add big red button to disable QUIC TLS resumption.

Protected by FLAGS_quic_disable_server_tls_resumption.

PiperOrigin-RevId: 341128029
Change-Id: Id6d81e63d8c5f58595565144c6af99525a5f25a6
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index 91af8c6..91e3dd1 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -5086,6 +5086,108 @@
   server_thread_->Resume();
 }
 
+TEST_P(EndToEndTest, TlsResumptionEnabledOnTheFly) {
+  SetQuicFlag(FLAGS_quic_disable_server_tls_resumption, true);
+  ASSERT_TRUE(Initialize());
+
+  if (!version_.UsesTls()) {
+    // This test is TLS specific.
+    return;
+  }
+
+  // Send the first request. Client should not have a resumption ticket.
+  SendSynchronousFooRequestAndCheckResponse();
+  QuicSpdyClientSession* client_session = GetClientSession();
+  ASSERT_TRUE(client_session);
+  EXPECT_EQ(client_session->GetCryptoStream()->EarlyDataReason(),
+            ssl_early_data_no_session_offered);
+  EXPECT_FALSE(client_session->EarlyDataAccepted());
+  client_->Disconnect();
+
+  SetQuicFlag(FLAGS_quic_disable_server_tls_resumption, false);
+
+  // Send the second request. Client should still have no resumption ticket, but
+  // it will receive one which can be used by the next request.
+  client_->Connect();
+  SendSynchronousFooRequestAndCheckResponse();
+
+  client_session = GetClientSession();
+  ASSERT_TRUE(client_session);
+  EXPECT_EQ(client_session->GetCryptoStream()->EarlyDataReason(),
+            ssl_early_data_no_session_offered);
+  EXPECT_FALSE(client_session->EarlyDataAccepted());
+  client_->Disconnect();
+
+  // Send the third request in 0RTT.
+  client_->Connect();
+  SendSynchronousFooRequestAndCheckResponse();
+
+  client_session = GetClientSession();
+  ASSERT_TRUE(client_session);
+  EXPECT_TRUE(client_session->EarlyDataAccepted());
+  client_->Disconnect();
+}
+
+TEST_P(EndToEndTest, TlsResumptionDisabledOnTheFly) {
+  SetQuicFlag(FLAGS_quic_disable_server_tls_resumption, false);
+  ASSERT_TRUE(Initialize());
+
+  if (!version_.UsesTls()) {
+    // This test is TLS specific.
+    return;
+  }
+
+  // Send the first request and then disconnect.
+  SendSynchronousFooRequestAndCheckResponse();
+  QuicSpdyClientSession* client_session = GetClientSession();
+  ASSERT_TRUE(client_session);
+  EXPECT_FALSE(client_session->EarlyDataAccepted());
+  client_->Disconnect();
+
+  // Send the second request in 0RTT.
+  client_->Connect();
+  SendSynchronousFooRequestAndCheckResponse();
+
+  client_session = GetClientSession();
+  ASSERT_TRUE(client_session);
+  EXPECT_TRUE(client_session->EarlyDataAccepted());
+  client_->Disconnect();
+
+  SetQuicFlag(FLAGS_quic_disable_server_tls_resumption, true);
+
+  // Send the third request. The client should try resumption but server should
+  // decline it.
+  client_->Connect();
+  SendSynchronousFooRequestAndCheckResponse();
+
+  client_session = GetClientSession();
+  ASSERT_TRUE(client_session);
+  EXPECT_FALSE(client_session->EarlyDataAccepted());
+  EXPECT_EQ(client_session->GetCryptoStream()->EarlyDataReason(),
+            ssl_early_data_session_not_resumed);
+  client_->Disconnect();
+
+  // Keep sending until the client runs out of resumption tickets.
+  for (int i = 0; i < 10; ++i) {
+    client_->Connect();
+    SendSynchronousFooRequestAndCheckResponse();
+
+    client_session = GetClientSession();
+    ASSERT_TRUE(client_session);
+    EXPECT_FALSE(client_session->EarlyDataAccepted());
+    const auto early_data_reason =
+        client_session->GetCryptoStream()->EarlyDataReason();
+    client_->Disconnect();
+
+    if (early_data_reason != ssl_early_data_session_not_resumed) {
+      EXPECT_EQ(early_data_reason, ssl_early_data_no_session_offered);
+      return;
+    }
+  }
+
+  ADD_FAILURE() << "Client should not have 10 resumption tickets.";
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/quic_protocol_flags_list.h b/quic/core/quic_protocol_flags_list.h
index c39d1e0..9484a23 100644
--- a/quic/core/quic_protocol_flags_list.h
+++ b/quic/core/quic_protocol_flags_list.h
@@ -237,4 +237,10 @@
                    false,
                    "If true, QUIC client with TLS will not try 0-RTT.")
 
+QUIC_PROTOCOL_FLAG(bool,
+                   quic_disable_server_tls_resumption,
+                   false,
+                   "If true, QUIC server will disable TLS resumption by not "
+                   "issuing or processing session tickets.")
+
 #endif
diff --git a/quic/core/tls_server_handshaker.cc b/quic/core/tls_server_handshaker.cc
index a935741..aa49597 100644
--- a/quic/core/tls_server_handshaker.cc
+++ b/quic/core/tls_server_handshaker.cc
@@ -98,6 +98,10 @@
 
   // Configure the SSL to be a server.
   SSL_set_accept_state(ssl());
+
+  if (GetQuicFlag(FLAGS_quic_disable_server_tls_resumption)) {
+    SSL_set_options(ssl(), SSL_OP_NO_TICKET);
+  }
 }
 
 TlsServerHandshaker::~TlsServerHandshaker() {
diff --git a/quic/core/tls_server_handshaker_test.cc b/quic/core/tls_server_handshaker_test.cc
index f890684..7411ba2 100644
--- a/quic/core/tls_server_handshaker_test.cc
+++ b/quic/core/tls_server_handshaker_test.cc
@@ -42,14 +42,39 @@
 const char kServerHostname[] = "test.example.com";
 const uint16_t kServerPort = 443;
 
-class TlsServerHandshakerTest : public QuicTestWithParam<ParsedQuicVersion> {
+struct TestParams {
+  ParsedQuicVersion version;
+  bool disable_resumption;
+};
+
+// Used by ::testing::PrintToStringParamName().
+std::string PrintToString(const TestParams& p) {
+  return quiche::QuicheStrCat(
+      ParsedQuicVersionToString(p.version), "_",
+      (p.disable_resumption ? "ResumptionDisabled" : "ResumptionEnabled"));
+}
+
+// Constructs test permutations.
+std::vector<TestParams> GetTestParams() {
+  std::vector<TestParams> params;
+  for (const auto& version : AllSupportedVersionsWithTls()) {
+    for (bool disable_resumption : {false, true}) {
+      params.push_back(TestParams{version, disable_resumption});
+    }
+  }
+  return params;
+}
+
+class TlsServerHandshakerTest : public QuicTestWithParam<TestParams> {
  public:
   TlsServerHandshakerTest()
       : server_compressed_certs_cache_(
             QuicCompressedCertsCache::kQuicCompressedCertsCacheSize),
         server_id_(kServerHostname, kServerPort, false),
-        supported_versions_({GetParam()}) {
+        supported_versions_({GetParam().version}) {
     SetQuicRestartFlag(quic_enable_zero_rtt_for_tls_v2, true);
+    SetQuicFlag(FLAGS_quic_disable_server_tls_resumption,
+                GetParam().disable_resumption);
     client_crypto_config_ = std::make_unique<QuicCryptoClientConfig>(
         crypto_test_utils::ProofVerifierForTesting(),
         std::make_unique<test::SimpleSessionCache>());
@@ -228,7 +253,7 @@
 
 INSTANTIATE_TEST_SUITE_P(TlsServerHandshakerTests,
                          TlsServerHandshakerTest,
-                         ::testing::ValuesIn(AllSupportedVersionsWithTls()),
+                         ::testing::ValuesIn(GetTestParams()),
                          ::testing::PrintToStringParamName());
 
 TEST_P(TlsServerHandshakerTest, NotInitiallyConected) {
@@ -369,9 +394,10 @@
   InitializeFakeClient();
   CompleteCryptoHandshake();
   ExpectHandshakeSuccessful();
-  EXPECT_TRUE(client_stream()->IsResumption());
-  EXPECT_TRUE(server_stream()->IsResumption());
-  EXPECT_TRUE(server_stream()->ResumptionAttempted());
+  EXPECT_NE(client_stream()->IsResumption(), GetParam().disable_resumption);
+  EXPECT_NE(server_stream()->IsResumption(), GetParam().disable_resumption);
+  EXPECT_NE(server_stream()->ResumptionAttempted(),
+            GetParam().disable_resumption);
 }
 
 TEST_P(TlsServerHandshakerTest, ResumptionWithAsyncDecryptCallback) {
@@ -386,6 +412,10 @@
   InitializeFakeClient();
 
   AdvanceHandshakeWithFakeClient();
+  if (GetParam().disable_resumption) {
+    ASSERT_EQ(ticket_crypter_->NumPendingCallbacks(), 0u);
+    return;
+  }
   // Test that the DecryptCallback will be run asynchronously, and then run it.
   ASSERT_EQ(ticket_crypter_->NumPendingCallbacks(), 1u);
   ticket_crypter_->RunPendingCallback(0);
@@ -398,6 +428,10 @@
 }
 
 TEST_P(TlsServerHandshakerTest, ResumptionWithFailingDecryptCallback) {
+  if (GetParam().disable_resumption) {
+    return;
+  }
+
   // Do the first handshake
   InitializeFakeClient();
   CompleteCryptoHandshake();
@@ -415,6 +449,10 @@
 }
 
 TEST_P(TlsServerHandshakerTest, ResumptionWithFailingAsyncDecryptCallback) {
+  if (GetParam().disable_resumption) {
+    return;
+  }
+
   // Do the first handshake
   InitializeFakeClient();
   CompleteCryptoHandshake();
@@ -469,8 +507,8 @@
   InitializeFakeClient();
   CompleteCryptoHandshake();
   ExpectHandshakeSuccessful();
-  EXPECT_TRUE(client_stream()->IsResumption());
-  EXPECT_TRUE(server_stream()->IsZeroRtt());
+  EXPECT_NE(client_stream()->IsResumption(), GetParam().disable_resumption);
+  EXPECT_NE(server_stream()->IsZeroRtt(), GetParam().disable_resumption);
 }
 
 TEST_P(TlsServerHandshakerTest, ZeroRttRejectOnApplicationStateChange) {
@@ -493,7 +531,7 @@
   InitializeFakeClient();
   CompleteCryptoHandshake();
   ExpectHandshakeSuccessful();
-  EXPECT_TRUE(client_stream()->IsResumption());
+  EXPECT_NE(client_stream()->IsResumption(), GetParam().disable_resumption);
   EXPECT_FALSE(server_stream()->IsZeroRtt());
 }