Ensure QuicTransportStream::Visitor::OnFinRead() is only called once.

This fixes a non-deterministic crash in Chromium tests: https://bugs.chromium.org/p/chromium/issues/detail?id=1065854.  This also fixes a potential bug in which the stream is never marked as closed.

gfe-relnote: n/a (code not used in production)
PiperOrigin-RevId: 303787933
Change-Id: I3c4f39e709e27dc5f81ccc78729323da1810fda5
diff --git a/quic/quic_transport/quic_transport_stream.cc b/quic/quic_transport/quic_transport_stream.cc
index 2824771..401ae7e 100644
--- a/quic/quic_transport/quic_transport_stream.cc
+++ b/quic/quic_transport/quic_transport_stream.cc
@@ -35,8 +35,8 @@
   iov.iov_base = buffer;
   iov.iov_len = buffer_size;
   const size_t result = sequencer()->Readv(&iov, 1);
-  if (sequencer()->IsClosed() && visitor_ != nullptr) {
-    visitor_->OnFinRead();
+  if (sequencer()->IsClosed()) {
+    MaybeNotifyFinRead();
   }
   return result;
 }
@@ -111,10 +111,7 @@
 
 void QuicTransportStream::OnDataAvailable() {
   if (sequencer()->IsClosed()) {
-    if (visitor_ != nullptr) {
-      visitor_->OnFinRead();
-    }
-    OnFinRead();
+    MaybeNotifyFinRead();
     return;
   }
 
@@ -138,4 +135,13 @@
   }
 }
 
+void QuicTransportStream::MaybeNotifyFinRead() {
+  if (visitor_ == nullptr || fin_read_notified_) {
+    return;
+  }
+  fin_read_notified_ = true;
+  visitor_->OnFinRead();
+  OnFinRead();
+}
+
 }  // namespace quic
diff --git a/quic/quic_transport/quic_transport_stream.h b/quic/quic_transport/quic_transport_stream.h
index d558cdb..f2bac88 100644
--- a/quic/quic_transport/quic_transport_stream.h
+++ b/quic/quic_transport/quic_transport_stream.h
@@ -63,8 +63,11 @@
   using QuicStream::WriteMemSlices;
   using QuicStream::WriteOrBufferData;
 
+  void MaybeNotifyFinRead();
+
   QuicTransportSessionInterface* session_interface_;
   std::unique_ptr<Visitor> visitor_ = nullptr;
+  bool fin_read_notified_ = false;
 };
 
 }  // namespace quic