Support 0-RTT TLS in QuicSpdySession

Protected by disabled flag quic_enable_zero_rtt_for_tls

PiperOrigin-RevId: 317031026
Change-Id: Ib65fbe5b34c63dd06c032c0a4171864ab988159e
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index e8c1643..9185bf5 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -204,6 +204,8 @@
     SetQuicReloadableFlag(quic_fix_server_pto_timeout, true);
 
     SetQuicReloadableFlag(quic_support_handshake_done_in_t050, true);
+    SetQuicReloadableFlag(quic_enable_tls_resumption, true);
+    SetQuicReloadableFlag(quic_enable_zero_rtt_for_tls, true);
   }
 
   ~EndToEndTest() override { QuicRecyclePort(server_address_.port()); }
@@ -1358,10 +1360,6 @@
   EXPECT_FALSE(client_->client()->ReceivedInchoateReject());
 
   client_->Disconnect();
-  if (version_.UsesTls()) {
-    // TODO(b/152551499): remove this when TLS supports 0-RTT.
-    return;
-  }
 
   // The 0-RTT handshake should succeed.
   client_->Connect();
@@ -1376,6 +1374,11 @@
   client_->Disconnect();
 
   // Restart the server so that the 0-RTT handshake will take 1 RTT.
+  if (version_.UsesTls()) {
+    // TODO(b/159168475): 0-RTT rejection in TLS currently doesn't work - stream
+    // data attempts to get retransmitted under ENCRYPTION_HANDSHAKE keys.
+    return;
+  }
   StopServer();
   server_writer_ = new PacketDroppingTestWriter();
   StartServer();
diff --git a/quic/core/http/quic_send_control_stream.cc b/quic/core/http/quic_send_control_stream.cc
index 64c306a..59574a1 100644
--- a/quic/core/http/quic_send_control_stream.cc
+++ b/quic/core/http/quic_send_control_stream.cc
@@ -18,18 +18,12 @@
 
 namespace quic {
 
-QuicSendControlStream::QuicSendControlStream(
-    QuicStreamId id,
-    QuicSpdySession* spdy_session,
-    uint64_t qpack_maximum_dynamic_table_capacity,
-    uint64_t qpack_maximum_blocked_streams,
-    uint64_t max_inbound_header_list_size)
+QuicSendControlStream::QuicSendControlStream(QuicStreamId id,
+                                             QuicSpdySession* spdy_session,
+                                             const SettingsFrame& settings)
     : QuicStream(id, spdy_session, /*is_static = */ true, WRITE_UNIDIRECTIONAL),
       settings_sent_(false),
-      qpack_maximum_dynamic_table_capacity_(
-          qpack_maximum_dynamic_table_capacity),
-      qpack_maximum_blocked_streams_(qpack_maximum_blocked_streams),
-      max_inbound_header_list_size_(max_inbound_header_list_size),
+      settings_(settings),
       spdy_session_(spdy_session) {}
 
 void QuicSendControlStream::OnStreamReset(const QuicRstStreamFrame& /*frame*/) {
@@ -56,13 +50,7 @@
   WriteOrBufferData(quiche::QuicheStringPiece(writer.data(), writer.length()),
                     false, nullptr);
 
-  SettingsFrame settings;
-  settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] =
-      qpack_maximum_dynamic_table_capacity_;
-  settings.values[SETTINGS_QPACK_BLOCKED_STREAMS] =
-      qpack_maximum_blocked_streams_;
-  settings.values[SETTINGS_MAX_HEADER_LIST_SIZE] =
-      max_inbound_header_list_size_;
+  SettingsFrame settings = settings_;
   // https://tools.ietf.org/html/draft-ietf-quic-http-25#section-7.2.4.1
   // specifies that setting identifiers of 0x1f * N + 0x21 are reserved and
   // greasing should be attempted.
diff --git a/quic/core/http/quic_send_control_stream.h b/quic/core/http/quic_send_control_stream.h
index 03bd1f7..c14254f 100644
--- a/quic/core/http/quic_send_control_stream.h
+++ b/quic/core/http/quic_send_control_stream.h
@@ -23,9 +23,7 @@
   // only be accessed through the session.
   QuicSendControlStream(QuicStreamId id,
                         QuicSpdySession* session,
-                        uint64_t qpack_maximum_dynamic_table_capacity,
-                        uint64_t qpack_maximum_blocked_streams,
-                        uint64_t max_inbound_header_list_size);
+                        const SettingsFrame& settings);
   QuicSendControlStream(const QuicSendControlStream&) = delete;
   QuicSendControlStream& operator=(const QuicSendControlStream&) = delete;
   ~QuicSendControlStream() override = default;
@@ -59,12 +57,8 @@
   // Track if a settings frame is already sent.
   bool settings_sent_;
 
-  // SETTINGS_QPACK_MAX_TABLE_CAPACITY value to send.
-  const uint64_t qpack_maximum_dynamic_table_capacity_;
-  // SETTINGS_QPACK_BLOCKED_STREAMS value to send.
-  const uint64_t qpack_maximum_blocked_streams_;
-  // SETTINGS_MAX_HEADER_LIST_SIZE value to send.
-  const uint64_t max_inbound_header_list_size_;
+  // SETTINGS values to send.
+  const SettingsFrame settings_;
 
   QuicSpdySession* const spdy_session_;
 };
diff --git a/quic/core/http/quic_server_session_base.cc b/quic/core/http/quic_server_session_base.cc
index 27db719..ef367d9 100644
--- a/quic/core/http/quic_server_session_base.cc
+++ b/quic/core/http/quic_server_session_base.cc
@@ -39,6 +39,9 @@
   crypto_stream_ =
       CreateQuicCryptoServerStream(crypto_config_, compressed_certs_cache_);
   QuicSpdySession::Initialize();
+  if (GetQuicReloadableFlag(quic_enable_zero_rtt_for_tls)) {
+    SendSettingsToCryptoStream();
+  }
 }
 
 void QuicServerSessionBase::OnConfigNegotiated() {
@@ -260,4 +263,19 @@
       bandwidth.ToBytesPerSecond(), std::numeric_limits<uint32_t>::max()));
 }
 
+void QuicServerSessionBase::SendSettingsToCryptoStream() {
+  if (!version().UsesTls()) {
+    return;
+  }
+  std::unique_ptr<char[]> buffer;
+  QuicByteCount buffer_size =
+      HttpEncoder::SerializeSettingsFrame(settings(), &buffer);
+
+  std::unique_ptr<ApplicationState> serialized_settings =
+      std::make_unique<ApplicationState>(buffer.get(),
+                                         buffer.get() + buffer_size);
+  GetMutableCryptoStream()->SetServerApplicationStateForResumption(
+      std::move(serialized_settings));
+}
+
 }  // namespace quic
diff --git a/quic/core/http/quic_server_session_base.h b/quic/core/http/quic_server_session_base.h
index bf7a5bb..b4a467f 100644
--- a/quic/core/http/quic_server_session_base.h
+++ b/quic/core/http/quic_server_session_base.h
@@ -99,6 +99,11 @@
   friend class test::QuicServerSessionBasePeer;
   friend class test::QuicSimpleServerSessionPeer;
 
+  // Informs the QuicCryptoStream of the SETTINGS that will be used on this
+  // connection, so that the server crypto stream knows whether to accept 0-RTT
+  // data.
+  void SendSettingsToCryptoStream();
+
   const QuicCryptoServerConfig* crypto_config_;
 
   // The cache which contains most recently compressed certs.
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc
index ddc9f74..e69a051 100644
--- a/quic/core/http/quic_spdy_session.cc
+++ b/quic/core/http/quic_spdy_session.cc
@@ -434,6 +434,7 @@
 void QuicSpdySession::Initialize() {
   QuicSession::Initialize();
 
+  FillSettingsFrame();
   if (!VersionUsesHttp3(transport_version())) {
     if (perspective() == Perspective::IS_SERVER) {
       set_largest_peer_created_stream_id(
@@ -464,6 +465,15 @@
       2 * max_inbound_header_list_size_);
 }
 
+void QuicSpdySession::FillSettingsFrame() {
+  settings_.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] =
+      qpack_maximum_dynamic_table_capacity_;
+  settings_.values[SETTINGS_QPACK_BLOCKED_STREAMS] =
+      qpack_maximum_blocked_streams_;
+  settings_.values[SETTINGS_MAX_HEADER_LIST_SIZE] =
+      max_inbound_header_list_size_;
+}
+
 void QuicSpdySession::OnDecoderStreamError(
     quiche::QuicheStringPiece error_message) {
   DCHECK(VersionUsesHttp3(transport_version()));
@@ -1204,9 +1214,7 @@
   DCHECK(VersionUsesHttp3(transport_version()));
   if (!send_control_stream_ && CanOpenNextOutgoingUnidirectionalStream()) {
     auto send_control = std::make_unique<QuicSendControlStream>(
-        GetNextOutgoingUnidirectionalStreamId(), this,
-        qpack_maximum_dynamic_table_capacity_, qpack_maximum_blocked_streams_,
-        max_inbound_header_list_size_);
+        GetNextOutgoingUnidirectionalStreamId(), this, settings_);
     send_control_stream_ = send_control.get();
     ActivateStream(std::move(send_control));
     if (debug_visitor_) {
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h
index b948002..4d6ee63 100644
--- a/quic/core/http/quic_spdy_session.h
+++ b/quic/core/http/quic_spdy_session.h
@@ -447,6 +447,8 @@
     return receive_control_stream_;
   }
 
+  const SettingsFrame& settings() const { return settings_; }
+
   // Initializes HTTP/3 unidirectional streams if not yet initialzed.
   virtual void MaybeInitializeHttp3UnidirectionalStreams();
 
@@ -480,6 +482,8 @@
   // Send a MAX_PUSH_ID frame.  Used in IETF QUIC only.
   void SendMaxPushId();
 
+  void FillSettingsFrame();
+
   std::unique_ptr<QpackEncoder> qpack_encoder_;
   std::unique_ptr<QpackDecoder> qpack_decoder_;
 
@@ -497,6 +501,8 @@
   QpackSendStream* qpack_encoder_send_stream_;
   QpackSendStream* qpack_decoder_send_stream_;
 
+  SettingsFrame settings_;
+
   // Maximum dynamic table capacity as defined at
   // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#maximum-dynamic-table-capacity
   // for the decoding context.  Value will be sent via