Accept empty frame with no FIN in IETF QUIC.

Protected by FLAGS_quic_reloadable_flag_quic_accept_empty_stream_frame_with_no_fin.

PiperOrigin-RevId: 348789512
Change-Id: I931a10c28d44dcacbfb5cf4bf2ef62b876e9c3c4
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index e4b5dc0..0d7152a 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -1907,10 +1907,12 @@
       GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0);
   // A bad stream frame with no data and no fin.
   QuicStreamFrame data1(stream_id1, false, 0, 0);
-  EXPECT_CALL(*connection_, CloseConnection(_, _, _))
-      .WillOnce(
-          Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
-  EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
+  if (!GetQuicReloadableFlag(quic_accept_empty_stream_frame_with_no_fin)) {
+    EXPECT_CALL(*connection_, CloseConnection(_, _, _))
+        .WillOnce(
+            Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
+    EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
+  }
   session_.OnStreamFrame(data1);
 }
 
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 8e99f37..04a2244 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -6,6 +6,7 @@
 
 QUIC_FLAG(FLAGS_quic_reloadable_flag_http2_use_fast_huffman_encoder, true)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_abort_qpack_on_stream_reset, true)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_accept_empty_stream_frame_with_no_fin, true)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_ack_delay_alarm_granularity, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_add_stream_info_to_idle_close_detail, true)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_allocate_stream_sequencer_buffer_blocks_on_demand, false)
diff --git a/quic/core/quic_stream.cc b/quic/core/quic_stream.cc
index add2715..4650f97 100644
--- a/quic/core/quic_stream.cc
+++ b/quic/core/quic_stream.cc
@@ -113,6 +113,7 @@
 
 PendingStream::PendingStream(QuicStreamId id, QuicSession* session)
     : id_(id),
+      version_(session->version()),
       stream_delegate_(session),
       stream_bytes_read_(0),
       fin_received_(false),
@@ -157,6 +158,10 @@
   return id_;
 }
 
+ParsedQuicVersion PendingStream::version() const {
+  return version_;
+}
+
 void PendingStream::OnStreamFrame(const QuicStreamFrame& frame) {
   DCHECK_EQ(frame.stream_id, id_);
 
@@ -897,6 +902,10 @@
   return send_buffer_.stream_offset() > stream_bytes_written();
 }
 
+ParsedQuicVersion QuicStream::version() const {
+  return session_->version();
+}
+
 QuicTransportVersion QuicStream::transport_version() const {
   return session_->transport_version();
 }
diff --git a/quic/core/quic_stream.h b/quic/core/quic_stream.h
index 50fc5e1..6138873 100644
--- a/quic/core/quic_stream.h
+++ b/quic/core/quic_stream.h
@@ -62,6 +62,7 @@
   void OnUnrecoverableError(QuicErrorCode error,
                             const std::string& details) override;
   QuicStreamId id() const override;
+  ParsedQuicVersion version() const override;
 
   // Buffers the contents of |frame|. Frame must have a non-zero offset.
   // If the data violates flow control, the connection will be closed.
@@ -90,6 +91,9 @@
   // ID of this stream.
   QuicStreamId id_;
 
+  // QUIC version being used by this stream.
+  ParsedQuicVersion version_;
+
   // |stream_delegate_| must outlive this stream.
   StreamDelegateInterface* stream_delegate_;
 
@@ -146,6 +150,7 @@
 
   // QuicStreamSequencer::StreamInterface implementation.
   QuicStreamId id() const override { return id_; }
+  ParsedQuicVersion version() const override;
   // Called by the stream subclass after it has consumed the final incoming
   // data.
   void OnFinRead() override;
diff --git a/quic/core/quic_stream_sequencer.cc b/quic/core/quic_stream_sequencer.cc
index a1861dc..dae5513 100644
--- a/quic/core/quic_stream_sequencer.cc
+++ b/quic/core/quic_stream_sequencer.cc
@@ -56,6 +56,14 @@
       (!CloseStreamAtOffset(frame.offset + data_len) || data_len == 0)) {
     return;
   }
+  if (GetQuicReloadableFlag(quic_accept_empty_stream_frame_with_no_fin)) {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_accept_empty_stream_frame_with_no_fin);
+    if (stream_->version().HasIetfQuicFrames() && data_len == 0) {
+      DCHECK(!frame.fin);
+      // Ignore empty frame with no fin.
+      return;
+    }
+  }
   OnFrameData(byte_offset, data_len, frame.data_buffer);
 }
 
diff --git a/quic/core/quic_stream_sequencer.h b/quic/core/quic_stream_sequencer.h
index a74526b..ec88c14 100644
--- a/quic/core/quic_stream_sequencer.h
+++ b/quic/core/quic_stream_sequencer.h
@@ -44,6 +44,9 @@
                                       const std::string& details) = 0;
     // Returns the stream id of this stream.
     virtual QuicStreamId id() const = 0;
+
+    // Returns the QUIC version being used by this stream.
+    virtual ParsedQuicVersion version() const = 0;
   };
 
   explicit QuicStreamSequencer(StreamInterface* quic_stream);
diff --git a/quic/core/quic_stream_sequencer_test.cc b/quic/core/quic_stream_sequencer_test.cc
index a996ee2..1aa614a 100644
--- a/quic/core/quic_stream_sequencer_test.cc
+++ b/quic/core/quic_stream_sequencer_test.cc
@@ -41,6 +41,9 @@
   MOCK_METHOD(void, AddBytesConsumed, (QuicByteCount bytes), (override));
 
   QuicStreamId id() const override { return 1; }
+  ParsedQuicVersion version() const override {
+    return CurrentSupportedVersions()[0];
+  }
 };
 
 namespace {
@@ -243,7 +246,11 @@
 }
 
 TEST_F(QuicStreamSequencerTest, EmptyFrame) {
-  EXPECT_CALL(stream_, OnUnrecoverableError(QUIC_EMPTY_STREAM_FRAME_NO_FIN, _));
+  if (!GetQuicReloadableFlag(quic_accept_empty_stream_frame_with_no_fin) ||
+      !stream_.version().HasIetfQuicFrames()) {
+    EXPECT_CALL(stream_,
+                OnUnrecoverableError(QUIC_EMPTY_STREAM_FRAME_NO_FIN, _));
+  }
   OnFrame(0, "");
   EXPECT_EQ(0u, NumBufferedBytes());
   EXPECT_EQ(0u, sequencer_->NumBytesConsumed());
diff --git a/quic/core/quic_stream_test.cc b/quic/core/quic_stream_test.cc
index 37d0247..2fb2187 100644
--- a/quic/core/quic_stream_test.cc
+++ b/quic/core/quic_stream_test.cc
@@ -1679,6 +1679,23 @@
   stream_->OnStreamReset(rst);
 }
 
+// Regression test for b/176073284.
+TEST_P(QuicStreamTest, EmptyStreamFrameWithNoFin) {
+  Initialize();
+  QuicStreamFrame empty_stream_frame(stream_->id(), false, 0, "");
+  if (GetQuicReloadableFlag(quic_accept_empty_stream_frame_with_no_fin) &&
+      stream_->version().HasIetfQuicFrames()) {
+    EXPECT_CALL(*connection_,
+                CloseConnection(QUIC_EMPTY_STREAM_FRAME_NO_FIN, _, _))
+        .Times(0);
+  } else {
+    EXPECT_CALL(*connection_,
+                CloseConnection(QUIC_EMPTY_STREAM_FRAME_NO_FIN, _, _));
+  }
+  EXPECT_CALL(*stream_, OnDataAvailable()).Times(0);
+  stream_->OnStreamFrame(empty_stream_frame);
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/tls_chlo_extractor.h b/quic/core/tls_chlo_extractor.h
index 0063808..ec7b115 100644
--- a/quic/core/tls_chlo_extractor.h
+++ b/quic/core/tls_chlo_extractor.h
@@ -179,6 +179,7 @@
   void OnUnrecoverableError(QuicErrorCode error,
                             const std::string& details) override;
   QuicStreamId id() const override { return 0; }
+  ParsedQuicVersion version() const override { return framer_->version(); }
 
  private:
   // Parses the length of the CHLO message by looking at the first four bytes.