Set up session config from 0-rtt transport parameters.

Protected by gfe2_reloadable_flag_quic_zero_rtt

PiperOrigin-RevId: 310435641
Change-Id: Ib9dd3e4563b2a53729b3a75554479b8f80538357
diff --git a/quic/core/crypto/tls_server_connection.cc b/quic/core/crypto/tls_server_connection.cc
index 7e5b366..54db545 100644
--- a/quic/core/crypto/tls_server_connection.cc
+++ b/quic/core/crypto/tls_server_connection.cc
@@ -4,6 +4,7 @@
 
 #include "net/third_party/quiche/src/quic/core/crypto/tls_server_connection.h"
 
+#include "third_party/boringssl/src/include/openssl/ssl.h"
 #include "net/third_party/quiche/src/quic/core/crypto/proof_source.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
 #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h"
@@ -27,6 +28,9 @@
       proof_source->GetTicketCrypter()) {
     SSL_CTX_set_ticket_aead_method(ssl_ctx.get(),
                                    &TlsServerConnection::kSessionTicketMethod);
+    if (GetQuicReloadableFlag(quic_enable_zero_rtt_for_tls)) {
+      SSL_CTX_set_early_data_enabled(ssl_ctx.get(), 1);
+    }
   } else {
     SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_NO_TICKET);
   }
diff --git a/quic/core/http/quic_spdy_client_session_test.cc b/quic/core/http/quic_spdy_client_session_test.cc
index 71604e6..45e90ad 100644
--- a/quic/core/http/quic_spdy_client_session_test.cc
+++ b/quic/core/http/quic_spdy_client_session_test.cc
@@ -11,9 +11,11 @@
 
 #include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h"
 #include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/http/http_constants.h"
 #include "net/third_party/quiche/src/quic/core/http/http_frames.h"
 #include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
 #include "net/third_party/quiche/src/quic/core/http/spdy_server_push_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
 #include "net/third_party/quiche/src/quic/core/quic_versions.h"
 #include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
@@ -170,6 +172,7 @@
       config.SetMaxBidirectionalStreamsToSend(server_max_incoming_streams);
     }
     SetQuicReloadableFlag(quic_enable_tls_resumption, true);
+    SetQuicReloadableFlag(quic_enable_zero_rtt_for_tls, true);
     std::unique_ptr<QuicCryptoServerConfig> crypto_config =
         crypto_test_utils::CryptoServerConfigForTesting();
     crypto_test_utils::HandshakeWithFakeServer(
@@ -177,6 +180,19 @@
         stream, AlpnForVersion(connection_->version()));
   }
 
+  void CreateConnection() {
+    connection_ = new PacketSavingConnection(&helper_, &alarm_factory_,
+                                             Perspective::IS_CLIENT,
+                                             SupportedVersions(GetParam()));
+    // Advance the time, because timers do not like uninitialized times.
+    connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+    session_ = std::make_unique<TestQuicSpdyClientSession>(
+        DefaultQuicConfig(), SupportedVersions(GetParam()), connection_,
+        QuicServerId(kServerHostname, kPort, false), crypto_config_.get(),
+        &push_promise_index_);
+    session_->Initialize();
+  }
+
   std::unique_ptr<QuicCryptoClientConfig> crypto_config_;
   MockQuicConnectionHelper helper_;
   MockAlarmFactory alarm_factory_;
@@ -966,6 +982,67 @@
                  ->application_state);
 }
 
+TEST_P(QuicSpdyClientSessionTest, IetfZeroRttSetup) {
+  // This feature is HTTP/3 only
+  if (!VersionUsesHttp3(session_->transport_version())) {
+    return;
+  }
+  CompleteCryptoHandshake();
+  EXPECT_FALSE(session_->GetCryptoStream()->IsResumption());
+  SettingsFrame settings;
+  settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = 2;
+  settings.values[SETTINGS_MAX_HEADER_LIST_SIZE] = 5;
+  settings.values[256] = 4;  // unknown setting
+  session_->OnSettingsFrame(settings);
+
+  CreateConnection();
+  EXPECT_EQ(0u, session_->flow_controller()->send_window_offset());
+  session_->CryptoConnect();
+
+  // The client session should have a basic setup ready before the handshake
+  // succeeds.
+  EXPECT_EQ(kInitialSessionFlowControlWindowForTest,
+            session_->flow_controller()->send_window_offset());
+  auto* id_manager = QuicSessionPeer::v99_streamid_manager(session_.get());
+  EXPECT_EQ(kDefaultMaxStreamsPerConnection,
+            id_manager->max_outgoing_bidirectional_streams());
+  EXPECT_EQ(
+      kDefaultMaxStreamsPerConnection + kHttp3StaticUnidirectionalStreamCount,
+      id_manager->max_outgoing_unidirectional_streams());
+  auto* control_stream =
+      QuicSpdySessionPeer::GetSendControlStream(session_.get());
+  EXPECT_EQ(kInitialStreamFlowControlWindowForTest,
+            control_stream->flow_controller()->send_window_offset());
+
+  // Complete the handshake with a different config.
+  QuicCryptoClientStream* stream =
+      static_cast<QuicCryptoClientStream*>(session_->GetMutableCryptoStream());
+  QuicConfig config = DefaultQuicConfig();
+  config.SetInitialMaxStreamDataBytesUnidirectionalToSend(
+      kInitialStreamFlowControlWindowForTest + 1);
+  config.SetInitialSessionFlowControlWindowToSend(
+      kInitialSessionFlowControlWindowForTest + 1);
+  config.SetMaxBidirectionalStreamsToSend(kDefaultMaxStreamsPerConnection + 1);
+  config.SetMaxUnidirectionalStreamsToSend(kDefaultMaxStreamsPerConnection + 1);
+  SetQuicReloadableFlag(quic_enable_tls_resumption, true);
+  std::unique_ptr<QuicCryptoServerConfig> crypto_config =
+      crypto_test_utils::CryptoServerConfigForTesting();
+  crypto_test_utils::HandshakeWithFakeServer(
+      &config, crypto_config.get(), &helper_, &alarm_factory_, connection_,
+      stream, AlpnForVersion(connection_->version()));
+
+  EXPECT_TRUE(session_->GetCryptoStream()->IsResumption());
+  EXPECT_EQ(kInitialSessionFlowControlWindowForTest + 1,
+            session_->flow_controller()->send_window_offset());
+  EXPECT_EQ(kDefaultMaxStreamsPerConnection + 1,
+            id_manager->max_outgoing_bidirectional_streams());
+  EXPECT_EQ(kDefaultMaxStreamsPerConnection +
+                kHttp3StaticUnidirectionalStreamCount + 1,
+            id_manager->max_outgoing_unidirectional_streams());
+  EXPECT_EQ(kInitialStreamFlowControlWindowForTest + 1,
+            control_stream->flow_controller()->send_window_offset());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/tls_client_handshaker.cc b/quic/core/tls_client_handshaker.cc
index c638040..a6aee11 100644
--- a/quic/core/tls_client_handshaker.cc
+++ b/quic/core/tls_client_handshaker.cc
@@ -12,6 +12,7 @@
 #include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
 #include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h"
 #include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_hostname_utils.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"
@@ -118,6 +119,20 @@
         session_cache_->Lookup(server_id_, SSL_get_SSL_CTX(ssl()));
     if (cached_state) {
       SSL_set_session(ssl(), cached_state->tls_session.get());
+      if (GetQuicReloadableFlag(quic_enable_zero_rtt_for_tls) &&
+          VersionHasIetfQuicFrames(session()->transport_version()) &&
+          SSL_SESSION_early_data_capable(cached_state->tls_session.get())) {
+        std::string error_details;
+        if (session()->config()->ProcessTransportParameters(
+                *(cached_state->transport_params), SERVER,
+                /*is_resumption = */ true, &error_details) != QUIC_NO_ERROR) {
+          QUIC_BUG << "Cached transport parameters failed to be parsed";
+          CloseConnection(QUIC_HANDSHAKE_FAILED,
+                          "Client failed to parse cached Transport Parameters");
+          return false;
+        }
+        session()->OnConfigNegotiated();
+      }
     }
   }
 
@@ -378,6 +393,7 @@
   int ssl_error = SSL_get_error(ssl(), rv);
   bool should_close = true;
   switch (state_) {
+    // TODO(b/153726130): handle the case where the server rejects early data.
     case STATE_HANDSHAKE_RUNNING:
       should_close = ssl_error != SSL_ERROR_WANT_READ;
       break;