gfe-relnote: Add SessionCache to TlsClientHandshaker, protected by reloadable flag quic_supports_tls_handshake

PiperOrigin-RevId: 279800830
Change-Id: Ib7b49726c14208f63c5b3a8c552cff36cb5d89bf
diff --git a/quic/core/crypto/quic_crypto_client_config.cc b/quic/core/crypto/quic_crypto_client_config.cc
index 6813f7c..6b00c4c 100644
--- a/quic/core/crypto/quic_crypto_client_config.cc
+++ b/quic/core/crypto/quic_crypto_client_config.cc
@@ -61,7 +61,13 @@
 
 QuicCryptoClientConfig::QuicCryptoClientConfig(
     std::unique_ptr<ProofVerifier> proof_verifier)
+    : QuicCryptoClientConfig(std::move(proof_verifier), nullptr) {}
+
+QuicCryptoClientConfig::QuicCryptoClientConfig(
+    std::unique_ptr<ProofVerifier> proof_verifier,
+    std::unique_ptr<SessionCache> session_cache)
     : proof_verifier_(std::move(proof_verifier)),
+      session_cache_(std::move(session_cache)),
       ssl_ctx_(TlsClientConnection::CreateSslCtx()) {
   DCHECK(proof_verifier_.get());
   SetDefaults();
@@ -850,6 +856,10 @@
   return proof_verifier_.get();
 }
 
+SessionCache* QuicCryptoClientConfig::session_cache() const {
+  return session_cache_.get();
+}
+
 SSL_CTX* QuicCryptoClientConfig::ssl_ctx() const {
   return ssl_ctx_.get();
 }
diff --git a/quic/core/crypto/quic_crypto_client_config.h b/quic/core/crypto/quic_crypto_client_config.h
index d3e627d..a3e1bcd 100644
--- a/quic/core/crypto/quic_crypto_client_config.h
+++ b/quic/core/crypto/quic_crypto_client_config.h
@@ -12,8 +12,10 @@
 #include <vector>
 
 #include "third_party/boringssl/src/include/openssl/base.h"
+#include "third_party/boringssl/src/include/openssl/ssl.h"
 #include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
 #include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h"
 #include "net/third_party/quiche/src/quic/core/quic_packets.h"
 #include "net/third_party/quiche/src/quic/core/quic_server_id.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
@@ -27,6 +29,53 @@
 class ProofVerifyDetails;
 class QuicRandom;
 
+// QuicResumptionState stores the state a client needs for performing connection
+// resumption.
+struct QUIC_EXPORT_PRIVATE QuicResumptionState {
+  // |tls_session| holds the cryptographic state necessary for a resumption. It
+  // includes the ALPN negotiated on the connection where the ticket was
+  // received.
+  bssl::UniquePtr<SSL_SESSION> tls_session;
+
+  // If the application using QUIC doesn't support 0-RTT handshakes or the
+  // client didn't receive a 0-RTT capable session ticket from the server,
+  // |transport_params| will be null. Otherwise, it will contain the transport
+  // parameters received from the server on the original connection.
+  std::unique_ptr<TransportParameters> transport_params;
+
+  // If |transport_params| is null, then |application_state| is ignored and
+  // should be empty. |application_state| contains serialized state that the
+  // client received from the server at the application layer that the client
+  // needs to remember when performing a 0-RTT handshake.
+  std::vector<uint8_t> application_state;
+};
+
+// SessionCache is an interface for managing storing and retrieving
+// QuicResumptionState structs.
+class QUIC_EXPORT_PRIVATE SessionCache {
+ public:
+  virtual ~SessionCache() {}
+
+  // Inserts |state| into the cache, keyed by |server_id|. Insert is called
+  // after a session ticket is received. If the session ticket is valid for
+  // 0-RTT, there may be a delay between its receipt and the call to Insert
+  // while waiting for application state for |state|.
+  //
+  // Insert may be called multiple times per connection. SessionCache
+  // implementations should support storing multiple entries per server ID.
+  virtual void Insert(const QuicServerId& server_id,
+                      std::unique_ptr<QuicResumptionState> state) = 0;
+
+  // Lookup is called once at the beginning of each TLS handshake to potentially
+  // provide the saved state both for the TLS handshake and for sending 0-RTT
+  // data (if supported). Lookup may return a nullptr. Implementations should
+  // delete cache entries after returning them in Lookup so that session tickets
+  // are used only once.
+  virtual std::unique_ptr<QuicResumptionState> Lookup(
+      const QuicServerId& server_id,
+      const SSL_CTX* ctx) = 0;
+};
+
 // QuicCryptoClientConfig contains crypto-related configuration settings for a
 // client. Note that this object isn't thread-safe. It's designed to be used on
 // a single thread at a time.
@@ -203,8 +252,11 @@
     virtual bool Matches(const QuicServerId& server_id) const = 0;
   };
 
+  // DEPRECATED: Use the constructor below instead.
   explicit QuicCryptoClientConfig(
       std::unique_ptr<ProofVerifier> proof_verifier);
+  QuicCryptoClientConfig(std::unique_ptr<ProofVerifier> proof_verifier,
+                         std::unique_ptr<SessionCache> session_cache);
   QuicCryptoClientConfig(const QuicCryptoClientConfig&) = delete;
   QuicCryptoClientConfig& operator=(const QuicCryptoClientConfig&) = delete;
   ~QuicCryptoClientConfig();
@@ -309,7 +361,7 @@
       std::string* error_details);
 
   ProofVerifier* proof_verifier() const;
-
+  SessionCache* session_cache() const;
   SSL_CTX* ssl_ctx() const;
 
   // Initialize the CachedState from |canonical_crypto_config| for the
@@ -388,6 +440,7 @@
   std::vector<std::string> canonical_suffixes_;
 
   std::unique_ptr<ProofVerifier> proof_verifier_;
+  std::unique_ptr<SessionCache> session_cache_;
   bssl::UniquePtr<SSL_CTX> ssl_ctx_;
 
   // The |user_agent_id_| passed in QUIC's CHLO message.
diff --git a/quic/core/crypto/tls_client_connection.cc b/quic/core/crypto/tls_client_connection.cc
index f28af66..98aa6e7 100644
--- a/quic/core/crypto/tls_client_connection.cc
+++ b/quic/core/crypto/tls_client_connection.cc
@@ -19,6 +19,11 @@
   // certificate after the connection is complete. We need to re-verify on
   // resumption in case of expiration or revocation/distrust.
   SSL_CTX_set_custom_verify(ssl_ctx.get(), SSL_VERIFY_PEER, &VerifyCallback);
+
+  // Configure session caching.
+  SSL_CTX_set_session_cache_mode(
+      ssl_ctx.get(), SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL);
+  SSL_CTX_sess_set_new_cb(ssl_ctx.get(), NewSessionCallback);
   return ssl_ctx;
 }
 
@@ -30,4 +35,11 @@
       ->delegate_->VerifyCert(out_alert);
 }
 
+// static
+int TlsClientConnection::NewSessionCallback(SSL* ssl, SSL_SESSION* session) {
+  static_cast<TlsClientConnection*>(ConnectionFromSsl(ssl))
+      ->delegate_->InsertSession(bssl::UniquePtr<SSL_SESSION>(session));
+  return 1;
+}
+
 }  // namespace quic
diff --git a/quic/core/crypto/tls_client_connection.h b/quic/core/crypto/tls_client_connection.h
index 6660343..035f420 100644
--- a/quic/core/crypto/tls_client_connection.h
+++ b/quic/core/crypto/tls_client_connection.h
@@ -26,6 +26,9 @@
     // or ssl_verify_retry if verification is happening asynchronously.
     virtual enum ssl_verify_result_t VerifyCert(uint8_t* out_alert) = 0;
 
+    // Called when a NewSessionTicket is received from the server.
+    virtual void InsertSession(bssl::UniquePtr<SSL_SESSION> session) = 0;
+
     // Provides the delegate for callbacks that are shared between client and
     // server.
     virtual TlsConnection::Delegate* ConnectionDelegate() = 0;
@@ -43,6 +46,10 @@
   // implementation is delegated to Delegate::VerifyCert.
   static enum ssl_verify_result_t VerifyCallback(SSL* ssl, uint8_t* out_alert);
 
+  // Registered as the callback for SSL_CTX_sess_set_new_cb, which calls
+  // Delegate::InsertSession.
+  static int NewSessionCallback(SSL* ssl, SSL_SESSION* session);
+
   Delegate* delegate_;
 };
 
diff --git a/quic/core/crypto/tls_server_connection.cc b/quic/core/crypto/tls_server_connection.cc
index 927c75a..f539a08 100644
--- a/quic/core/crypto/tls_server_connection.cc
+++ b/quic/core/crypto/tls_server_connection.cc
@@ -16,6 +16,7 @@
   SSL_CTX_set_tlsext_servername_callback(ssl_ctx.get(),
                                          &SelectCertificateCallback);
   SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), &SelectAlpnCallback, nullptr);
+  SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_NO_TICKET);
   return ssl_ctx;
 }
 
diff --git a/quic/core/http/quic_spdy_client_session_test.cc b/quic/core/http/quic_spdy_client_session_test.cc
index d5b186c..200024f 100644
--- a/quic/core/http/quic_spdy_client_session_test.cc
+++ b/quic/core/http/quic_spdy_client_session_test.cc
@@ -166,8 +166,10 @@
       config.SetMaxIncomingBidirectionalStreamsToSend(
           server_max_incoming_streams);
     }
+    QuicCryptoServerConfig crypto_config =
+        crypto_test_utils::CryptoServerConfigForTesting();
     crypto_test_utils::HandshakeWithFakeServer(
-        &config, &helper_, &alarm_factory_, connection_, stream,
+        &config, &crypto_config, &helper_, &alarm_factory_, connection_, stream,
         AlpnForVersion(connection_->version()));
   }
 
diff --git a/quic/core/quic_crypto_client_stream.cc b/quic/core/quic_crypto_client_stream.cc
index 354debb..b30cd3a 100644
--- a/quic/core/quic_crypto_client_stream.cc
+++ b/quic/core/quic_crypto_client_stream.cc
@@ -43,9 +43,8 @@
       break;
     case PROTOCOL_TLS1_3:
       handshaker_ = std::make_unique<TlsClientHandshaker>(
-          this, session, server_id, crypto_config->proof_verifier(),
-          crypto_config->ssl_ctx(), std::move(verify_context), proof_handler,
-          crypto_config->user_agent_id());
+          server_id, this, session, std::move(verify_context), crypto_config,
+          proof_handler);
       break;
     case PROTOCOL_UNSUPPORTED:
       QUIC_BUG << "Attempting to create QuicCryptoClientStream for unknown "
diff --git a/quic/core/quic_crypto_client_stream_test.cc b/quic/core/quic_crypto_client_stream_test.cc
index 2622439..675dc20 100644
--- a/quic/core/quic_crypto_client_stream_test.cc
+++ b/quic/core/quic_crypto_client_stream_test.cc
@@ -22,6 +22,7 @@
 #include "net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_peer.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
 #include "net/third_party/quiche/src/quic/test_tools/simple_quic_framer.h"
+#include "net/third_party/quiche/src/quic/test_tools/simple_session_cache.h"
 
 using testing::_;
 
@@ -37,7 +38,10 @@
   QuicCryptoClientStreamTest()
       : supported_versions_(AllSupportedVersions()),
         server_id_(kServerHostname, kServerPort, false),
-        crypto_config_(crypto_test_utils::ProofVerifierForTesting()) {
+        crypto_config_(crypto_test_utils::ProofVerifierForTesting(),
+                       std::make_unique<test::SimpleSessionCache>()),
+        server_crypto_config_(
+            crypto_test_utils::CryptoServerConfigForTesting()) {
     CreateConnection();
   }
 
@@ -56,6 +60,17 @@
             {AlpnForVersion(connection_->version())})));
   }
 
+  void UseTlsHandshake() {
+    SetQuicReloadableFlag(quic_supports_tls_handshake, true);
+    supported_versions_.clear();
+    for (ParsedQuicVersion version : AllSupportedVersions()) {
+      if (version.handshake_protocol != PROTOCOL_TLS1_3) {
+        continue;
+      }
+      supported_versions_.push_back(version);
+    }
+  }
+
   void CompleteCryptoHandshake() {
     if (stream()->handshake_protocol() != PROTOCOL_TLS1_3) {
       EXPECT_CALL(*session_, OnProofValid(testing::_));
@@ -65,8 +80,8 @@
     stream()->CryptoConnect();
     QuicConfig config;
     crypto_test_utils::HandshakeWithFakeServer(
-        &config, &server_helper_, &alarm_factory_, connection_, stream(),
-        AlpnForVersion(connection_->version()));
+        &config, &server_crypto_config_, &server_helper_, &alarm_factory_,
+        connection_, stream(), AlpnForVersion(connection_->version()));
   }
 
   QuicCryptoClientStream* stream() {
@@ -82,6 +97,7 @@
   QuicServerId server_id_;
   CryptoHandshakeMessage message_;
   QuicCryptoClientConfig crypto_config_;
+  QuicCryptoServerConfig server_crypto_config_;
 };
 
 TEST_F(QuicCryptoClientStreamTest, NotInitiallyConected) {
@@ -97,14 +113,7 @@
 }
 
 TEST_F(QuicCryptoClientStreamTest, ConnectedAfterTlsHandshake) {
-  SetQuicReloadableFlag(quic_supports_tls_handshake, true);
-  supported_versions_.clear();
-  for (ParsedQuicVersion version : AllSupportedVersions()) {
-    if (version.handshake_protocol != PROTOCOL_TLS1_3) {
-      continue;
-    }
-    supported_versions_.push_back(version);
-  }
+  UseTlsHandshake();
   CreateConnection();
   CompleteCryptoHandshake();
   EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol());
@@ -115,27 +124,44 @@
 
 TEST_F(QuicCryptoClientStreamTest,
        ProofVerifyDetailsAvailableAfterTlsHandshake) {
-  SetQuicReloadableFlag(quic_supports_tls_handshake, true);
-  supported_versions_.clear();
-  for (ParsedQuicVersion version : AllSupportedVersions()) {
-    if (version.handshake_protocol != PROTOCOL_TLS1_3) {
-      continue;
-    }
-    supported_versions_.push_back(version);
-  }
+  UseTlsHandshake();
   CreateConnection();
 
   EXPECT_CALL(*session_, OnProofVerifyDetailsAvailable(testing::_));
   stream()->CryptoConnect();
   QuicConfig config;
   crypto_test_utils::HandshakeWithFakeServer(
-      &config, &server_helper_, &alarm_factory_, connection_, stream(),
-      AlpnForVersion(connection_->version()));
+      &config, &server_crypto_config_, &server_helper_, &alarm_factory_,
+      connection_, stream(), AlpnForVersion(connection_->version()));
   EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol());
   EXPECT_TRUE(stream()->encryption_established());
   EXPECT_TRUE(stream()->handshake_confirmed());
 }
 
+TEST_F(QuicCryptoClientStreamTest, TlsResumption) {
+  UseTlsHandshake();
+  // Enable resumption on the server:
+  SSL_CTX_clear_options(server_crypto_config_.ssl_ctx(), SSL_OP_NO_TICKET);
+  CreateConnection();
+
+  // Finish establishing the first connection:
+  CompleteCryptoHandshake();
+
+  EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol());
+  EXPECT_TRUE(stream()->encryption_established());
+  EXPECT_TRUE(stream()->handshake_confirmed());
+  EXPECT_FALSE(stream()->IsResumption());
+
+  // Create a second connection
+  CreateConnection();
+  CompleteCryptoHandshake();
+
+  EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol());
+  EXPECT_TRUE(stream()->encryption_established());
+  EXPECT_TRUE(stream()->handshake_confirmed());
+  EXPECT_TRUE(stream()->IsResumption());
+}
+
 TEST_F(QuicCryptoClientStreamTest, MessageAfterHandshake) {
   CompleteCryptoHandshake();
 
diff --git a/quic/core/tls_client_handshaker.cc b/quic/core/tls_client_handshaker.cc
index 2056953..85a278c 100644
--- a/quic/core/tls_client_handshaker.cc
+++ b/quic/core/tls_client_handshaker.cc
@@ -43,22 +43,21 @@
 }
 
 TlsClientHandshaker::TlsClientHandshaker(
+    const QuicServerId& server_id,
     QuicCryptoStream* stream,
     QuicSession* session,
-    const QuicServerId& server_id,
-    ProofVerifier* proof_verifier,
-    SSL_CTX* ssl_ctx,
     std::unique_ptr<ProofVerifyContext> verify_context,
-    QuicCryptoClientStream::ProofHandler* proof_handler,
-    const std::string& user_agent_id)
-    : TlsHandshaker(stream, session, ssl_ctx),
+    QuicCryptoClientConfig* crypto_config,
+    QuicCryptoClientStream::ProofHandler* proof_handler)
+    : TlsHandshaker(stream, session, crypto_config->ssl_ctx()),
       server_id_(server_id),
-      proof_verifier_(proof_verifier),
+      proof_verifier_(crypto_config->proof_verifier()),
       verify_context_(std::move(verify_context)),
       proof_handler_(proof_handler),
-      user_agent_id_(user_agent_id),
+      session_cache_(crypto_config->session_cache()),
+      user_agent_id_(crypto_config->user_agent_id()),
       crypto_negotiated_params_(new QuicCryptoNegotiatedParameters),
-      tls_connection_(ssl_ctx, this) {}
+      tls_connection_(crypto_config->ssl_ctx(), this) {}
 
 TlsClientHandshaker::~TlsClientHandshaker() {
   if (proof_verify_callback_) {
@@ -87,6 +86,15 @@
     return false;
   }
 
+  // Set a session to resume, if there is one.
+  if (session_cache_) {
+    std::unique_ptr<QuicResumptionState> cached_state =
+        session_cache_->Lookup(server_id_, SSL_get_SSL_CTX(ssl()));
+    if (cached_state) {
+      SSL_set_session(ssl(), cached_state->tls_session.get());
+    }
+  }
+
   // Start the handshake.
   AdvanceHandshake();
   return session()->connection()->connected();
@@ -199,8 +207,7 @@
 
 bool TlsClientHandshaker::IsResumption() const {
   QUIC_BUG_IF(!handshake_confirmed_);
-  // We don't support resumption (yet).
-  return false;
+  return SSL_session_reused(ssl()) == 1;
 }
 
 int TlsClientHandshaker::num_scup_messages_received() const {
@@ -246,7 +253,10 @@
     return;
   }
   if (state_ == STATE_HANDSHAKE_COMPLETE) {
-    // TODO(nharper): Handle post-handshake messages.
+    int rv = SSL_process_quic_post_handshake(ssl());
+    if (rv != 1) {
+      CloseConnection(QUIC_HANDSHAKE_FAILED, "Unexpected post-handshake data");
+    }
     return;
   }
 
@@ -394,4 +404,14 @@
   }
 }
 
+void TlsClientHandshaker::InsertSession(bssl::UniquePtr<SSL_SESSION> session) {
+  if (session_cache_ == nullptr) {
+    QUIC_DVLOG(1) << "No session cache, not inserting a session";
+    return;
+  }
+  auto cache_state = std::make_unique<QuicResumptionState>();
+  cache_state->tls_session = std::move(session);
+  session_cache_->Insert(server_id_, std::move(cache_state));
+}
+
 }  // namespace quic
diff --git a/quic/core/tls_client_handshaker.h b/quic/core/tls_client_handshaker.h
index 17b529e..0b473a3 100644
--- a/quic/core/tls_client_handshaker.h
+++ b/quic/core/tls_client_handshaker.h
@@ -24,14 +24,12 @@
       public QuicCryptoClientStream::HandshakerDelegate,
       public TlsClientConnection::Delegate {
  public:
-  TlsClientHandshaker(QuicCryptoStream* stream,
+  TlsClientHandshaker(const QuicServerId& server_id,
+                      QuicCryptoStream* stream,
                       QuicSession* session,
-                      const QuicServerId& server_id,
-                      ProofVerifier* proof_verifier,
-                      SSL_CTX* ssl_ctx,
                       std::unique_ptr<ProofVerifyContext> verify_context,
-                      QuicCryptoClientStream::ProofHandler* proof_handler,
-                      const std::string& user_agent_id);
+                      QuicCryptoClientConfig* crypto_config,
+                      QuicCryptoClientStream::ProofHandler* proof_handler);
   TlsClientHandshaker(const TlsClientHandshaker&) = delete;
   TlsClientHandshaker& operator=(const TlsClientHandshaker&) = delete;
 
@@ -101,6 +99,8 @@
   bool ProcessTransportParameters(std::string* error_details);
   void FinishHandshake();
 
+  void InsertSession(bssl::UniquePtr<SSL_SESSION> session) override;
+
   QuicServerId server_id_;
 
   // Objects used for verifying the server's certificate chain.
@@ -113,6 +113,10 @@
   // certificate verification.
   QuicCryptoClientStream::ProofHandler* proof_handler_;
 
+  // Used for session resumption. |session_cache_| is owned by the
+  // QuicCryptoClientConfig passed into TlsClientHandshaker's constructor.
+  SessionCache* session_cache_;
+
   std::string user_agent_id_;
 
   // ProofVerifierCallback used for async certificate verification. This object
diff --git a/quic/core/tls_handshaker_test.cc b/quic/core/tls_handshaker_test.cc
index 96691fa..d3463a8 100644
--- a/quic/core/tls_handshaker_test.cc
+++ b/quic/core/tls_handshaker_test.cc
@@ -223,17 +223,15 @@
  public:
   explicit TestQuicCryptoClientStream(QuicSession* session)
       : TestQuicCryptoStream(session),
-        proof_verifier_(new FakeProofVerifier),
-        ssl_ctx_(TlsClientConnection::CreateSslCtx()),
+        crypto_config_(std::make_unique<FakeProofVerifier>(),
+                       /*session_cache*/ nullptr),
         handshaker_(new TlsClientHandshaker(
+            QuicServerId("test.example.com", 443, false),
             this,
             session,
-            QuicServerId("test.example.com", 443, false),
-            proof_verifier_.get(),
-            ssl_ctx_.get(),
             crypto_test_utils::ProofVerifyContextForTesting(),
-            &proof_handler_,
-            "quic-tester")) {}
+            &crypto_config_,
+            &proof_handler_)) {}
 
   ~TestQuicCryptoClientStream() override = default;
 
@@ -244,13 +242,12 @@
   bool CryptoConnect() { return handshaker_->CryptoConnect(); }
 
   FakeProofVerifier* GetFakeProofVerifier() const {
-    return proof_verifier_.get();
+    return static_cast<FakeProofVerifier*>(crypto_config_.proof_verifier());
   }
 
  private:
-  std::unique_ptr<FakeProofVerifier> proof_verifier_;
   MockProofHandler proof_handler_;
-  bssl::UniquePtr<SSL_CTX> ssl_ctx_;
+  QuicCryptoClientConfig crypto_config_;
   std::unique_ptr<TlsClientHandshaker> handshaker_;
 };
 
diff --git a/quic/quic_transport/quic_transport_client_session_test.cc b/quic/quic_transport/quic_transport_client_session_test.cc
index 1d9f05e..91aecdb 100644
--- a/quic/quic_transport/quic_transport_client_session_test.cc
+++ b/quic/quic_transport/quic_transport_client_session_test.cc
@@ -76,9 +76,11 @@
   void Connect() {
     session_->CryptoConnect();
     QuicConfig server_config = DefaultQuicConfig();
+    QuicCryptoServerConfig crypto_config =
+        crypto_test_utils::CryptoServerConfigForTesting();
     crypto_test_utils::HandshakeWithFakeServer(
-        &server_config, &helper_, &alarm_factory_, &connection_, crypto_stream_,
-        QuicTransportAlpn());
+        &server_config, &crypto_config, &helper_, &alarm_factory_, &connection_,
+        crypto_stream_, QuicTransportAlpn());
   }
 
   MockAlarmFactory alarm_factory_;
diff --git a/quic/test_tools/crypto_test_utils.cc b/quic/test_tools/crypto_test_utils.cc
index f38ea81..e2dc56c 100644
--- a/quic/test_tools/crypto_test_utils.cc
+++ b/quic/test_tools/crypto_test_utils.cc
@@ -209,7 +209,14 @@
 
 }  // namespace
 
+QuicCryptoServerConfig CryptoServerConfigForTesting() {
+  return QuicCryptoServerConfig(
+      QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance(),
+      ProofSourceForTesting(), KeyExchangeSource::Default());
+}
+
 int HandshakeWithFakeServer(QuicConfig* server_quic_config,
+                            QuicCryptoServerConfig* crypto_config,
                             MockQuicConnectionHelper* helper,
                             MockAlarmFactory* alarm_factory,
                             PacketSavingConnection* client_conn,
@@ -219,17 +226,14 @@
       helper, alarm_factory, Perspective::IS_SERVER,
       ParsedVersionOfIndex(client_conn->supported_versions(), 0));
 
-  QuicCryptoServerConfig crypto_config(
-      QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance(),
-      ProofSourceForTesting(), KeyExchangeSource::Default());
   QuicCompressedCertsCache compressed_certs_cache(
       QuicCompressedCertsCache::kQuicCompressedCertsCacheSize);
   SetupCryptoServerConfigForTest(
-      server_conn->clock(), server_conn->random_generator(), &crypto_config);
+      server_conn->clock(), server_conn->random_generator(), crypto_config);
 
   TestQuicSpdyServerSession server_session(
       server_conn, *server_quic_config, client_conn->supported_versions(),
-      &crypto_config, &compressed_certs_cache);
+      crypto_config, &compressed_certs_cache);
   server_session.OnSuccessfulVersionNegotiation(
       client_conn->supported_versions().front());
   EXPECT_CALL(*server_session.helper(),
@@ -346,7 +350,8 @@
     MovePackets(client_conn, &client_i, server, server_conn,
                 Perspective::IS_SERVER);
 
-    if (client->handshake_confirmed() && server->handshake_confirmed()) {
+    if (client->handshake_confirmed() && server->handshake_confirmed() &&
+        server_conn->encrypted_packets_.size() == server_i) {
       break;
     }
     ASSERT_GT(server_conn->encrypted_packets_.size(), server_i);
diff --git a/quic/test_tools/crypto_test_utils.h b/quic/test_tools/crypto_test_utils.h
index 6f87e90..2071614 100644
--- a/quic/test_tools/crypto_test_utils.h
+++ b/quic/test_tools/crypto_test_utils.h
@@ -64,8 +64,13 @@
   bool only_tls_versions = false;
 };
 
+// Returns a QuicCryptoServerConfig that is in a reasonable configuration to
+// pass into HandshakeWithFakeServer.
+QuicCryptoServerConfig CryptoServerConfigForTesting();
+
 // returns: the number of client hellos that the client sent.
 int HandshakeWithFakeServer(QuicConfig* server_quic_config,
+                            QuicCryptoServerConfig* crypto_config,
                             MockQuicConnectionHelper* helper,
                             MockAlarmFactory* alarm_factory,
                             PacketSavingConnection* client_conn,
diff --git a/quic/test_tools/simple_session_cache.cc b/quic/test_tools/simple_session_cache.cc
new file mode 100644
index 0000000..7787fbe
--- /dev/null
+++ b/quic/test_tools/simple_session_cache.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/simple_session_cache.h"
+
+namespace quic {
+namespace test {
+
+void SimpleSessionCache::Insert(const QuicServerId& server_id,
+                                std::unique_ptr<QuicResumptionState> state) {
+  cache_entries_.insert(std::make_pair(server_id, std::move(state)));
+}
+
+std::unique_ptr<QuicResumptionState> SimpleSessionCache::Lookup(
+    const QuicServerId& server_id,
+    const SSL_CTX* /*ctx*/) {
+  auto it = cache_entries_.find(server_id);
+  if (it == cache_entries_.end()) {
+    return nullptr;
+  }
+  std::unique_ptr<QuicResumptionState> state = std::move(it->second);
+  cache_entries_.erase(it);
+  return state;
+}
+
+}  // namespace test
+}  // namespace quic
diff --git a/quic/test_tools/simple_session_cache.h b/quic/test_tools/simple_session_cache.h
new file mode 100644
index 0000000..40a6946
--- /dev/null
+++ b/quic/test_tools/simple_session_cache.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMPLE_SESSION_CACHE_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMPLE_SESSION_CACHE_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h"
+
+namespace quic {
+namespace test {
+
+// SimpleSessionCache provides a simple implementation of SessionCache that
+// stores only one QuicResumptionState per QuicServerId. No limit is placed on
+// the total number of entries in the cache. When Lookup is called, if a cache
+// entry exists for the provided QuicServerId, the entry will be removed from
+// the cached when it is returned.
+class SimpleSessionCache : public SessionCache {
+ public:
+  SimpleSessionCache() = default;
+  ~SimpleSessionCache() override = default;
+
+  void Insert(const QuicServerId& server_id,
+              std::unique_ptr<QuicResumptionState> state) override;
+  std::unique_ptr<QuicResumptionState> Lookup(const QuicServerId& server_id,
+                                              const SSL_CTX* ctx) override;
+
+ private:
+  std::map<QuicServerId, std::unique_ptr<QuicResumptionState>> cache_entries_;
+};
+
+}  // namespace test
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_TEST_TOOLS_SIMPLE_SESSION_CACHE_H_