Add support for accepting 0-RTT in TlsServerHandshaker

This adds support at the crypto handshake layer for supporting 0-RTT TLS
handshakes on the server. Part of this support includes receiving a signal
from the application layer, via a new method
QuicCryptoStream::SetServerApplicationStateForResumption. This method
replaces the previously client-only
QuicCryptoClientStream::OnApplicationState.

Introduce quic 0-rtt tls support, protected by quic_enable_zero_rtt_for_tls

PiperOrigin-RevId: 315331343
Change-Id: Ife83cf526be38bd4f5c8a3de0e6cd4c40be6f7ae
diff --git a/quic/core/http/quic_spdy_client_session_base.cc b/quic/core/http/quic_spdy_client_session_base.cc
index 04f8594..86592bb 100644
--- a/quic/core/http/quic_spdy_client_session_base.cc
+++ b/quic/core/http/quic_spdy_client_session_base.cc
@@ -240,8 +240,8 @@
       HttpEncoder::SerializeSettingsFrame(frame, &buffer);
   auto serialized_data = std::make_unique<ApplicationState>(
       buffer.get(), buffer.get() + frame_length);
-  static_cast<QuicCryptoClientStreamBase*>(GetMutableCryptoStream())
-      ->OnApplicationState(std::move(serialized_data));
+  GetMutableCryptoStream()->SetServerApplicationStateForResumption(
+      std::move(serialized_data));
 }
 
 }  // namespace quic
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc
index 4376d8a..cb6a7f6 100644
--- a/quic/core/http/quic_spdy_session.cc
+++ b/quic/core/http/quic_spdy_session.cc
@@ -857,7 +857,7 @@
                                 ConnectionCloseBehavior::SILENT_CLOSE);
 }
 
-bool QuicSpdySession::SetApplicationState(ApplicationState* cached_state) {
+bool QuicSpdySession::ResumeApplicationState(ApplicationState* cached_state) {
   DCHECK_EQ(perspective(), Perspective::IS_CLIENT);
   DCHECK(VersionUsesHttp3(transport_version()));
 
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h
index cdd81a0..069cceb 100644
--- a/quic/core/http/quic_spdy_session.h
+++ b/quic/core/http/quic_spdy_session.h
@@ -378,7 +378,7 @@
   void OnStreamCreated(QuicSpdyStream* stream);
 
   // Decode SETTINGS from |cached_state| and apply it to the session.
-  bool SetApplicationState(ApplicationState* cached_state) override;
+  bool ResumeApplicationState(ApplicationState* cached_state) override;
 
  protected:
   // Override CreateIncomingStream(), CreateOutgoingBidirectionalStream() and
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index 8223617..7b4e0d7 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -140,6 +140,8 @@
   HandshakeState GetHandshakeState() const override {
     return one_rtt_keys_available() ? HANDSHAKE_COMPLETE : HANDSHAKE_START;
   }
+  void SetServerApplicationStateForResumption(
+      std::unique_ptr<ApplicationState> /*application_state*/) override {}
   const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
       const override {
     return *params_;
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc
index 7ab9aa0..e769a9d 100644
--- a/quic/core/http/quic_spdy_stream_test.cc
+++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -127,6 +127,8 @@
   HandshakeState GetHandshakeState() const override {
     return one_rtt_keys_available() ? HANDSHAKE_COMPLETE : HANDSHAKE_START;
   }
+  void SetServerApplicationStateForResumption(
+      std::unique_ptr<ApplicationState> /*application_state*/) override {}
   const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
       const override {
     return *params_;
diff --git a/quic/core/quic_crypto_client_handshaker.h b/quic/core/quic_crypto_client_handshaker.h
index f99d36e..90f011d 100644
--- a/quic/core/quic_crypto_client_handshaker.h
+++ b/quic/core/quic_crypto_client_handshaker.h
@@ -55,7 +55,7 @@
   void OnConnectionClosed(QuicErrorCode /*error*/,
                           ConnectionCloseSource /*source*/) override;
   void OnHandshakeDoneReceived() override;
-  void OnApplicationState(
+  void SetServerApplicationStateForResumption(
       std::unique_ptr<ApplicationState> /*application_state*/) override {
     QUICHE_NOTREACHED();
   }
diff --git a/quic/core/quic_crypto_client_stream.cc b/quic/core/quic_crypto_client_stream.cc
index 36dc4cd..62a261d 100644
--- a/quic/core/quic_crypto_client_stream.cc
+++ b/quic/core/quic_crypto_client_stream.cc
@@ -126,9 +126,10 @@
   handshaker_->OnHandshakeDoneReceived();
 }
 
-void QuicCryptoClientStream::OnApplicationState(
+void QuicCryptoClientStream::SetServerApplicationStateForResumption(
     std::unique_ptr<ApplicationState> application_state) {
-  handshaker_->OnApplicationState(std::move(application_state));
+  handshaker_->SetServerApplicationStateForResumption(
+      std::move(application_state));
 }
 
 }  // namespace quic
diff --git a/quic/core/quic_crypto_client_stream.h b/quic/core/quic_crypto_client_stream.h
index 23f83c7..be99fb2 100644
--- a/quic/core/quic_crypto_client_stream.h
+++ b/quic/core/quic_crypto_client_stream.h
@@ -63,9 +63,6 @@
   // client.  Does not count update messages that were received prior
   // to handshake confirmation.
   virtual int num_scup_messages_received() const = 0;
-
-  virtual void OnApplicationState(
-      std::unique_ptr<ApplicationState> application_state) = 0;
 };
 
 class QUIC_EXPORT_PRIVATE QuicCryptoClientStream
@@ -167,7 +164,7 @@
     virtual void OnHandshakeDoneReceived() = 0;
 
     // Called when application state is received.
-    virtual void OnApplicationState(
+    virtual void SetServerApplicationStateForResumption(
         std::unique_ptr<ApplicationState> application_state) = 0;
   };
 
@@ -223,10 +220,9 @@
                           ConnectionCloseSource source) override;
   void OnHandshakeDoneReceived() override;
   HandshakeState GetHandshakeState() const override;
-  size_t BufferSizeLimitForLevel(EncryptionLevel level) const override;
-
-  void OnApplicationState(
+  void SetServerApplicationStateForResumption(
       std::unique_ptr<ApplicationState> application_state) override;
+  size_t BufferSizeLimitForLevel(EncryptionLevel level) const override;
 
   std::string chlo_hash() const;
 
diff --git a/quic/core/quic_crypto_server_stream.cc b/quic/core/quic_crypto_server_stream.cc
index c7dd2d9..b21745c 100644
--- a/quic/core/quic_crypto_server_stream.cc
+++ b/quic/core/quic_crypto_server_stream.cc
@@ -375,6 +375,12 @@
   return one_rtt_packet_decrypted_ ? HANDSHAKE_COMPLETE : HANDSHAKE_START;
 }
 
+void QuicCryptoServerStream::SetServerApplicationStateForResumption(
+    std::unique_ptr<ApplicationState> /*state*/) {
+  // QUIC Crypto doesn't need to remember any application state as part of doing
+  // 0-RTT resumption, so this function is a no-op.
+}
+
 size_t QuicCryptoServerStream::BufferSizeLimitForLevel(
     EncryptionLevel level) const {
   return QuicCryptoHandshaker::BufferSizeLimitForLevel(level);
diff --git a/quic/core/quic_crypto_server_stream.h b/quic/core/quic_crypto_server_stream.h
index 3aaf0ef..9ed7764 100644
--- a/quic/core/quic_crypto_server_stream.h
+++ b/quic/core/quic_crypto_server_stream.h
@@ -56,6 +56,8 @@
       const override;
   CryptoMessageParser* crypto_message_parser() override;
   HandshakeState GetHandshakeState() const override;
+  void SetServerApplicationStateForResumption(
+      std::unique_ptr<ApplicationState> state) override;
   size_t BufferSizeLimitForLevel(EncryptionLevel level) const override;
 
   // From QuicCryptoHandshaker
diff --git a/quic/core/quic_crypto_stream.h b/quic/core/quic_crypto_stream.h
index 49d3704..54d1b25 100644
--- a/quic/core/quic_crypto_stream.h
+++ b/quic/core/quic_crypto_stream.h
@@ -100,6 +100,21 @@
   // Returns current handshake state.
   virtual HandshakeState GetHandshakeState() const = 0;
 
+  // Called to provide the server-side application state that must be checked
+  // when performing a 0-RTT TLS resumption.
+  //
+  // On a client, this may be called at any time; 0-RTT tickets will not be
+  // cached until this function is called. When a 0-RTT resumption is attempted,
+  // QuicSession::SetApplicationState will be called with the state provided by
+  // a call to this function on a previous connection.
+  //
+  // On a server, this function must be called before commencing the handshake,
+  // otherwise 0-RTT tickets will not be issued. On subsequent connections,
+  // 0-RTT will be rejected if the data passed into this function does not match
+  // the data passed in on the connection where the 0-RTT ticket was issued.
+  virtual void SetServerApplicationStateForResumption(
+      std::unique_ptr<ApplicationState> state) = 0;
+
   // Returns the maximum number of bytes that can be buffered at a particular
   // encryption level |level|.
   virtual size_t BufferSizeLimitForLevel(EncryptionLevel level) const;
diff --git a/quic/core/quic_crypto_stream_test.cc b/quic/core/quic_crypto_stream_test.cc
index 3f086f9..f763d2d 100644
--- a/quic/core/quic_crypto_stream_test.cc
+++ b/quic/core/quic_crypto_stream_test.cc
@@ -61,6 +61,8 @@
   void OnHandshakePacketSent() override {}
   void OnHandshakeDoneReceived() override {}
   HandshakeState GetHandshakeState() const override { return HANDSHAKE_START; }
+  void SetServerApplicationStateForResumption(
+      std::unique_ptr<ApplicationState> /*application_state*/) override {}
 
  private:
   QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_;
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index c9e3216..61019a4 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -461,15 +461,15 @@
   // Called on clients by the crypto handshaker to provide application state
   // necessary for sending application data in 0-RTT. The state provided here is
   // the same state that was provided to the crypto handshaker in
-  // QuicCryptoClientStream::OnApplicationState on a previous connection.
-  // Application protocols that require state to be carried over from the
-  // previous connection to support 0-RTT data must implement this method to
-  // ingest this state. For example, an HTTP/3 QuicSession would implement this
-  // function to process the remembered server SETTINGS frame and apply those
-  // SETTINGS to 0-RTT data. This function returns true if the application state
-  // has been successfully processed, and false if there was an error processing
-  // the cached state and the connection should be closed.
-  virtual bool SetApplicationState(ApplicationState* /*cached_state*/) {
+  // QuicCryptoStream::SetServerApplicationStateForResumption on a previous
+  // connection. Application protocols that require state to be carried over
+  // from the previous connection to support 0-RTT data must implement this
+  // method to ingest this state. For example, an HTTP/3 QuicSession would
+  // implement this function to process the remembered server SETTINGS and apply
+  // those SETTINGS to 0-RTT data. This function returns true if the application
+  // state has been successfully processed, and false if there was an error
+  // processing the cached state and the connection should be closed.
+  virtual bool ResumeApplicationState(ApplicationState* /*cached_state*/) {
     return true;
   }
 
diff --git a/quic/core/quic_session_test.cc b/quic/core/quic_session_test.cc
index 258bfa2..5f0f9b7 100644
--- a/quic/core/quic_session_test.cc
+++ b/quic/core/quic_session_test.cc
@@ -134,6 +134,8 @@
   HandshakeState GetHandshakeState() const override {
     return one_rtt_keys_available() ? HANDSHAKE_COMPLETE : HANDSHAKE_START;
   }
+  void SetServerApplicationStateForResumption(
+      std::unique_ptr<ApplicationState> /*application_state*/) override {}
 
   MOCK_METHOD(void, OnCanWrite, (), (override));
   bool HasPendingCryptoRetransmission() const override { return false; }
diff --git a/quic/core/tls_client_handshaker.cc b/quic/core/tls_client_handshaker.cc
index 864040c..cf754ec 100644
--- a/quic/core/tls_client_handshaker.cc
+++ b/quic/core/tls_client_handshaker.cc
@@ -149,7 +149,7 @@
   session()->OnConfigNegotiated();
 
   if (has_application_state_) {
-    if (!session()->SetApplicationState(cached_state->application_state)) {
+    if (!session()->ResumeApplicationState(cached_state->application_state)) {
       QUIC_BUG << "Unable to parse cached application state.";
       CloseConnection(QUIC_HANDSHAKE_FAILED,
                       "Client failed to parse cached application state.");
@@ -617,7 +617,7 @@
   TlsHandshaker::WriteMessage(level, data);
 }
 
-void TlsClientHandshaker::OnApplicationState(
+void TlsClientHandshaker::SetServerApplicationStateForResumption(
     std::unique_ptr<ApplicationState> application_state) {
   DCHECK_EQ(STATE_HANDSHAKE_COMPLETE, state_);
   received_application_state_ = std::move(application_state);
diff --git a/quic/core/tls_client_handshaker.h b/quic/core/tls_client_handshaker.h
index ad35ba1..573c055 100644
--- a/quic/core/tls_client_handshaker.h
+++ b/quic/core/tls_client_handshaker.h
@@ -71,7 +71,7 @@
   void WriteMessage(EncryptionLevel level,
                     quiche::QuicheStringPiece data) override;
 
-  void OnApplicationState(
+  void SetServerApplicationStateForResumption(
       std::unique_ptr<ApplicationState> application_state) override;
 
   void AllowEmptyAlpnForTests() { allow_empty_alpn_for_tests_ = true; }
diff --git a/quic/core/tls_handshaker_test.cc b/quic/core/tls_handshaker_test.cc
index 5a2bd64..caf9a9c 100644
--- a/quic/core/tls_handshaker_test.cc
+++ b/quic/core/tls_handshaker_test.cc
@@ -192,6 +192,8 @@
   HandshakeState GetHandshakeState() const override {
     return handshaker()->GetHandshakeState();
   }
+  void SetServerApplicationStateForResumption(
+      std::unique_ptr<ApplicationState> /*application_state*/) override {}
 
   const std::vector<std::pair<std::string, EncryptionLevel>>& pending_writes() {
     return pending_writes_;
diff --git a/quic/core/tls_server_handshaker.cc b/quic/core/tls_server_handshaker.cc
index 60aa64f..057e86d 100644
--- a/quic/core/tls_server_handshaker.cc
+++ b/quic/core/tls_server_handshaker.cc
@@ -15,6 +15,7 @@
 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
 #include "net/third_party/quiche/src/common/platform/api/quiche_arraysize.h"
 #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h"
+#include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h"
 
 namespace quic {
 
@@ -95,15 +96,6 @@
 
   // Configure the SSL to be a server.
   SSL_set_accept_state(ssl());
-
-  if (GetQuicReloadableFlag(quic_enable_zero_rtt_for_tls)) {
-    // TODO(b/152551499): Properly set early data context. This change is to
-    // temporarily unblock QuicSpdyClientSessionTest.IetfZeroRttSetup which
-    // assumes that the server will sent an early data capable ticket, and then
-    // accept early data on resumption.
-    uint8_t context[] = {0};
-    SSL_set_quic_early_data_context(ssl(), context, QUICHE_ARRAYSIZE(context));
-  }
 }
 
 TlsServerHandshaker::~TlsServerHandshaker() {
@@ -133,8 +125,7 @@
 }
 
 bool TlsServerHandshaker::IsZeroRtt() const {
-  // TODO(nharper): Support 0-RTT with TLS 1.3 in QUIC.
-  return false;
+  return SSL_early_data_accepted(ssl());
 }
 
 bool TlsServerHandshaker::IsResumption() const {
@@ -207,6 +198,11 @@
   return HANDSHAKE_START;
 }
 
+void TlsServerHandshaker::SetServerApplicationStateForResumption(
+    std::unique_ptr<ApplicationState> state) {
+  application_state_ = std::move(state);
+}
+
 size_t TlsServerHandshaker::BufferSizeLimitForLevel(
     EncryptionLevel level) const {
   return TlsHandshaker::BufferSizeLimitForLevel(level);
@@ -329,6 +325,17 @@
                                     server_params_bytes.size()) != 1) {
     return false;
   }
+  if (application_state_) {
+    std::vector<uint8_t> early_data_context;
+    if (!SerializeTransportParametersForTicket(
+            server_params, *application_state_, &early_data_context)) {
+      QUIC_BUG << "Failed to serialize Transport Parameters for ticket.";
+      return false;
+    }
+    SSL_set_quic_early_data_context(ssl(), early_data_context.data(),
+                                    early_data_context.size());
+    application_state_.reset(nullptr);
+  }
   return true;
 }
 
diff --git a/quic/core/tls_server_handshaker.h b/quic/core/tls_server_handshaker.h
index 9d4bc5c..13b734e 100644
--- a/quic/core/tls_server_handshaker.h
+++ b/quic/core/tls_server_handshaker.h
@@ -61,6 +61,8 @@
       const override;
   CryptoMessageParser* crypto_message_parser() override;
   HandshakeState GetHandshakeState() const override;
+  void SetServerApplicationStateForResumption(
+      std::unique_ptr<ApplicationState> state) override;
   size_t BufferSizeLimitForLevel(EncryptionLevel level) const override;
   void SetWriteSecret(EncryptionLevel level,
                       const SSL_CIPHER* cipher,
@@ -185,6 +187,8 @@
   std::string cert_verify_sig_;
   std::unique_ptr<ProofSource::Details> proof_source_details_;
 
+  std::unique_ptr<ApplicationState> application_state_;
+
   // Pre-shared key used during the handshake.
   std::string pre_shared_key_;
 
diff --git a/quic/core/tls_server_handshaker_test.cc b/quic/core/tls_server_handshaker_test.cc
index d91df58..70550fc 100644
--- a/quic/core/tls_server_handshaker_test.cc
+++ b/quic/core/tls_server_handshaker_test.cc
@@ -47,9 +47,12 @@
   TlsServerHandshakerTest()
       : server_compressed_certs_cache_(
             QuicCompressedCertsCache::kQuicCompressedCertsCacheSize),
-        server_id_(kServerHostname, kServerPort, false),
-        client_crypto_config_(crypto_test_utils::ProofVerifierForTesting(),
-                              std::make_unique<test::SimpleSessionCache>()) {
+        server_id_(kServerHostname, kServerPort, false) {
+    SetQuicReloadableFlag(quic_enable_tls_resumption, true);
+    SetQuicReloadableFlag(quic_enable_zero_rtt_for_tls, true);
+    client_crypto_config_ = std::make_unique<QuicCryptoClientConfig>(
+        crypto_test_utils::ProofVerifierForTesting(),
+        std::make_unique<test::SimpleSessionCache>());
     InitializeServerConfig();
     InitializeServer();
     InitializeFakeClient();
@@ -65,7 +68,6 @@
   }
 
   void InitializeServerConfig() {
-    SetQuicReloadableFlag(quic_enable_tls_resumption, true);
     auto ticket_crypter = std::make_unique<TestTicketCrypter>();
     ticket_crypter_ = ticket_crypter.get();
     auto proof_source = std::make_unique<FakeProofSource>();
@@ -126,7 +128,7 @@
     CreateClientSessionForTest(
         server_id_, QuicTime::Delta::FromSeconds(100000), supported_versions_,
         helpers_.back().get(), alarm_factories_.back().get(),
-        &client_crypto_config_, &client_connection_, &client_session);
+        client_crypto_config_.get(), &client_connection_, &client_session);
     const std::string default_alpn =
         AlpnForVersion(client_connection_->version());
     ON_CALL(*client_session, GetAlpnsToOffer())
@@ -212,7 +214,7 @@
 
   // Client state.
   PacketSavingConnection* client_connection_;
-  QuicCryptoClientConfig client_crypto_config_;
+  std::unique_ptr<QuicCryptoClientConfig> client_crypto_config_;
   std::unique_ptr<TestQuicSpdyClientSession> client_session_;
 
   crypto_test_utils::FakeClientOptions client_options_;
@@ -472,6 +474,53 @@
   EXPECT_EQ(moved_messages_counts_.second, 0u);
 }
 
+TEST_F(TlsServerHandshakerTest, ZeroRttResumption) {
+  std::vector<uint8_t> application_state = {0, 1, 2, 3};
+
+  // Do the first handshake
+  server_stream()->SetServerApplicationStateForResumption(
+      std::make_unique<ApplicationState>(application_state));
+  InitializeFakeClient();
+  CompleteCryptoHandshake();
+  ExpectHandshakeSuccessful();
+  EXPECT_FALSE(client_stream()->IsResumption());
+  EXPECT_FALSE(server_stream()->IsZeroRtt());
+
+  // Now do another handshake
+  InitializeServer();
+  server_stream()->SetServerApplicationStateForResumption(
+      std::make_unique<ApplicationState>(application_state));
+  InitializeFakeClient();
+  CompleteCryptoHandshake();
+  ExpectHandshakeSuccessful();
+  EXPECT_TRUE(client_stream()->IsResumption());
+  EXPECT_TRUE(server_stream()->IsZeroRtt());
+}
+
+TEST_F(TlsServerHandshakerTest, ZeroRttRejectOnApplicationStateChange) {
+  std::vector<uint8_t> original_application_state = {1, 2};
+  std::vector<uint8_t> new_application_state = {3, 4};
+
+  // Do the first handshake
+  server_stream()->SetServerApplicationStateForResumption(
+      std::make_unique<ApplicationState>(original_application_state));
+  InitializeFakeClient();
+  CompleteCryptoHandshake();
+  ExpectHandshakeSuccessful();
+  EXPECT_FALSE(client_stream()->IsResumption());
+  EXPECT_FALSE(server_stream()->IsZeroRtt());
+
+  // Do another handshake, but change the application state
+  InitializeServer();
+  server_stream()->SetServerApplicationStateForResumption(
+      std::make_unique<ApplicationState>(new_application_state));
+  InitializeFakeClient();
+  CompleteCryptoHandshake();
+  ExpectHandshakeSuccessful();
+  EXPECT_TRUE(client_stream()->IsResumption());
+  EXPECT_FALSE(server_stream()->IsZeroRtt());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/test_tools/crypto_test_utils.cc b/quic/test_tools/crypto_test_utils.cc
index ddeba47..eacbca5 100644
--- a/quic/test_tools/crypto_test_utils.cc
+++ b/quic/test_tools/crypto_test_utils.cc
@@ -242,7 +242,12 @@
   TestQuicSpdyServerSession server_session(
       server_conn, *server_quic_config, client_conn->supported_versions(),
       crypto_config, &compressed_certs_cache);
+  // Call SetServerApplicationStateForResumption so that the fake server
+  // supports 0-RTT in TLS.
   server_session.Initialize();
+  server_session.GetMutableCryptoStream()
+      ->SetServerApplicationStateForResumption(
+          std::make_unique<ApplicationState>());
   EXPECT_CALL(*server_session.helper(),
               CanAcceptClientHello(testing::_, testing::_, testing::_,
                                    testing::_, testing::_))
diff --git a/quic/test_tools/quic_test_utils.cc b/quic/test_tools/quic_test_utils.cc
index ed668d7..bc10611 100644
--- a/quic/test_tools/quic_test_utils.cc
+++ b/quic/test_tools/quic_test_utils.cc
@@ -756,8 +756,8 @@
                                 &push_promise_index_,
                                 config,
                                 supported_versions) {
-  // TODO(b/153726130): Consider adding OnApplicationState calls in tests and
-  // set |has_application_state| to true.
+  // TODO(b/153726130): Consider adding SetServerApplicationStateForResumption
+  // calls in tests and set |has_application_state| to true.
   crypto_stream_ = std::make_unique<QuicCryptoClientStream>(
       server_id, this, crypto_test_utils::ProofVerifyContextForTesting(),
       crypto_config, this, /*has_application_state = */ false);
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
index 39f052b..8bf8549 100644
--- a/quic/test_tools/quic_test_utils.h
+++ b/quic/test_tools/quic_test_utils.h
@@ -839,6 +839,8 @@
   void OnHandshakePacketSent() override {}
   void OnHandshakeDoneReceived() override {}
   HandshakeState GetHandshakeState() const override { return HANDSHAKE_START; }
+  void SetServerApplicationStateForResumption(
+      std::unique_ptr<ApplicationState> /*application_state*/) override {}
 
  private:
   QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_;