Prevent QUIC streams from closing the connection directly.

Instead the error is passed through a delegate to the QUIC session to handle.

gfe-relnote: no behavior change. not protected.
PiperOrigin-RevId: 294729094
Change-Id: I4f0fc7b4ddbcd34ddf549ae4d053cbcc78535987
diff --git a/quic/core/http/quic_receive_control_stream.cc b/quic/core/http/quic_receive_control_stream.cc
index c66e696..ab7a55b 100644
--- a/quic/core/http/quic_receive_control_stream.cc
+++ b/quic/core/http/quic_receive_control_stream.cc
@@ -145,10 +145,9 @@
 
  private:
   void CloseConnectionOnWrongFrame(quiche::QuicheStringPiece frame_type) {
-    stream_->session()->connection()->CloseConnection(
+    stream_->CloseConnectionWithDetails(
         QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM,
-        quiche::QuicheStrCat(frame_type, " frame received on control stream"),
-        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+        quiche::QuicheStrCat(frame_type, " frame received on control stream"));
   }
 
   QuicReceiveControlStream* stream_;
@@ -171,9 +170,8 @@
     const QuicRstStreamFrame& /*frame*/) {
   // TODO(renjietang) Change the error code to H/3 specific
   // HTTP_CLOSED_CRITICAL_STREAM.
-  session()->connection()->CloseConnection(
-      QUIC_INVALID_STREAM_ID, "Attempt to reset receive control stream",
-      ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+  stream_delegate()->OnStreamError(QUIC_INVALID_STREAM_ID,
+                                   "Attempt to reset receive control stream");
 }
 
 void QuicReceiveControlStream::OnDataAvailable() {
@@ -200,9 +198,8 @@
     QuicByteCount /* header_length */) {
   if (settings_frame_received_) {
     // TODO(renjietang): Change error code to HTTP_UNEXPECTED_FRAME.
-    session()->connection()->CloseConnection(
-        QUIC_INVALID_STREAM_ID, "Settings frames are received twice.",
-        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+    stream_delegate()->OnStreamError(QUIC_INVALID_STREAM_ID,
+                                     "Settings frames are received twice.");
     return false;
   }
 
@@ -226,10 +223,9 @@
 bool QuicReceiveControlStream::OnPriorityUpdateFrameStart(
     QuicByteCount /* header_length */) {
   if (!settings_frame_received_) {
-    session()->connection()->CloseConnection(
+    stream_delegate()->OnStreamError(
         QUIC_INVALID_STREAM_ID,
-        "PRIORITY_UPDATE frame received before SETTINGS.",
-        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+        "PRIORITY_UPDATE frame received before SETTINGS.");
     return false;
   }
   return true;
@@ -255,10 +251,9 @@
     int urgency;
     if (!quiche::QuicheTextUtils::StringToInt(value, &urgency) || urgency < 0 ||
         urgency > 7) {
-      session()->connection()->CloseConnection(
+      stream_delegate()->OnStreamError(
           QUIC_INVALID_STREAM_ID,
-          "Invalid value for PRIORITY_UPDATE urgency parameter.",
-          ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+          "Invalid value for PRIORITY_UPDATE urgency parameter.");
       return false;
     }
 
diff --git a/quic/core/http/quic_send_control_stream.cc b/quic/core/http/quic_send_control_stream.cc
index 79ea11b..0d8616c 100644
--- a/quic/core/http/quic_send_control_stream.cc
+++ b/quic/core/http/quic_send_control_stream.cc
@@ -34,9 +34,8 @@
 void QuicSendControlStream::OnStreamReset(const QuicRstStreamFrame& /*frame*/) {
   // TODO(renjietang) Change the error code to H/3 specific
   // HTTP_CLOSED_CRITICAL_STREAM.
-  session()->connection()->CloseConnection(
-      QUIC_INVALID_STREAM_ID, "Attempt to reset send control stream",
-      ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+  stream_delegate()->OnStreamError(QUIC_INVALID_STREAM_ID,
+                                   "Attempt to reset send control stream");
 }
 
 void QuicSendControlStream::MaybeSendSettingsFrame() {
diff --git a/quic/core/http/quic_spdy_stream.cc b/quic/core/http/quic_spdy_stream.cc
index 89748a9..67234a9 100644
--- a/quic/core/http/quic_spdy_stream.cc
+++ b/quic/core/http/quic_spdy_stream.cc
@@ -41,9 +41,8 @@
   HttpDecoderVisitor& operator=(const HttpDecoderVisitor&) = delete;
 
   void OnError(HttpDecoder* decoder) override {
-    stream_->session()->connection()->CloseConnection(
-        decoder->error(), decoder->error_detail(),
-        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+    stream_->CloseConnectionWithDetails(decoder->error(),
+                                        decoder->error_detail());
   }
 
   bool OnCancelPushFrame(const CancelPushFrame& /*frame*/) override {
@@ -170,10 +169,9 @@
 
  private:
   void CloseConnectionOnWrongFrame(quiche::QuicheStringPiece frame_type) {
-    stream_->session()->connection()->CloseConnection(
+    stream_->CloseConnectionWithDetails(
         QUIC_HTTP_FRAME_UNEXPECTED_ON_SPDY_STREAM,
-        quiche::QuicheStrCat(frame_type, " frame received on data stream"),
-        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+        quiche::QuicheStrCat(frame_type, " frame received on data stream"));
   }
 
   QuicSpdyStream* stream_;
@@ -649,9 +647,8 @@
     const QuicHeaderList& /*header_list */) {
   // To be overridden in QuicSpdyClientStream.  Not supported on
   // server side.
-  session()->connection()->CloseConnection(
-      QUIC_INVALID_HEADERS_STREAM_DATA, "Promise headers received by server",
-      ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+  stream_delegate()->OnStreamError(QUIC_INVALID_HEADERS_STREAM_DATA,
+                                   "Promise headers received by server");
 }
 
 void QuicSpdyStream::OnTrailingHeadersComplete(
@@ -663,18 +660,16 @@
   if (!VersionUsesHttp3(transport_version()) && fin_received()) {
     QUIC_DLOG(INFO) << ENDPOINT
                     << "Received Trailers after FIN, on stream: " << id();
-    session()->connection()->CloseConnection(
-        QUIC_INVALID_HEADERS_STREAM_DATA, "Trailers after fin",
-        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+    stream_delegate()->OnStreamError(QUIC_INVALID_HEADERS_STREAM_DATA,
+                                     "Trailers after fin");
     return;
   }
 
   if (!VersionUsesHttp3(transport_version()) && !fin) {
     QUIC_DLOG(INFO) << ENDPOINT
                     << "Trailers must have FIN set, on stream: " << id();
-    session()->connection()->CloseConnection(
-        QUIC_INVALID_HEADERS_STREAM_DATA, "Fin missing from trailers",
-        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+    stream_delegate()->OnStreamError(QUIC_INVALID_HEADERS_STREAM_DATA,
+                                     "Fin missing from trailers");
     return;
   }
 
@@ -685,9 +680,8 @@
                                           &received_trailers_)) {
     QUIC_DLOG(ERROR) << ENDPOINT << "Trailers for stream " << id()
                      << " are malformed.";
-    session()->connection()->CloseConnection(
-        QUIC_INVALID_HEADERS_STREAM_DATA, "Trailers are malformed",
-        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+    stream_delegate()->OnStreamError(QUIC_INVALID_HEADERS_STREAM_DATA,
+                                     "Trailers are malformed");
     return;
   }
   trailers_decompressed_ = true;
@@ -858,9 +852,8 @@
   DCHECK(VersionUsesHttp3(transport_version()));
   if (!headers_decompressed_ || trailers_decompressed_) {
     // TODO(b/124216424): Change error code to HTTP_UNEXPECTED_FRAME.
-    session()->connection()->CloseConnection(
-        QUIC_INVALID_HEADERS_STREAM_DATA, "Unexpected DATA frame received.",
-        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+    stream_delegate()->OnStreamError(QUIC_INVALID_HEADERS_STREAM_DATA,
+                                     "Unexpected DATA frame received.");
     return false;
   }
 
@@ -938,10 +931,9 @@
 
   if (trailers_decompressed_) {
     // TODO(b/124216424): Change error code to HTTP_UNEXPECTED_FRAME.
-    session()->connection()->CloseConnection(
+    stream_delegate()->OnStreamError(
         QUIC_INVALID_HEADERS_STREAM_DATA,
-        "HEADERS frame received after trailing HEADERS.",
-        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+        "HEADERS frame received after trailing HEADERS.");
     return false;
   }
 
diff --git a/quic/core/qpack/qpack_receive_stream.cc b/quic/core/qpack/qpack_receive_stream.cc
index a5df828..f896ca4 100644
--- a/quic/core/qpack/qpack_receive_stream.cc
+++ b/quic/core/qpack/qpack_receive_stream.cc
@@ -16,9 +16,8 @@
 void QpackReceiveStream::OnStreamReset(const QuicRstStreamFrame& /*frame*/) {
   // TODO(renjietang) Change the error code to H/3 specific
   // HTTP_CLOSED_CRITICAL_STREAM.
-  session()->connection()->CloseConnection(
-      QUIC_INVALID_STREAM_ID, "Attempt to reset Qpack receive stream",
-      ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+  stream_delegate()->OnStreamError(QUIC_INVALID_STREAM_ID,
+                                   "Attempt to reset Qpack receive stream");
 }
 
 void QpackReceiveStream::OnDataAvailable() {
diff --git a/quic/core/qpack/qpack_send_stream.cc b/quic/core/qpack/qpack_send_stream.cc
index a42d0d0..57493e3 100644
--- a/quic/core/qpack/qpack_send_stream.cc
+++ b/quic/core/qpack/qpack_send_stream.cc
@@ -19,9 +19,8 @@
 void QpackSendStream::OnStreamReset(const QuicRstStreamFrame& /*frame*/) {
   // TODO(renjietang) Change the error code to H/3 specific
   // HTTP_CLOSED_CRITICAL_STREAM.
-  session()->connection()->CloseConnection(
-      QUIC_INVALID_STREAM_ID, "Attempt to reset qpack send stream",
-      ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+  stream_delegate()->OnStreamError(QUIC_INVALID_STREAM_ID,
+                                   "Attempt to reset qpack send stream");
 }
 
 void QpackSendStream::WriteStreamData(quiche::QuicheStringPiece data) {
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index ebd0380..a85ab2d 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -839,6 +839,13 @@
       ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
 }
 
+void QuicSession::OnStreamError(QuicErrorCode error_code,
+                                std::string error_details) {
+  connection_->CloseConnection(
+      error_code, error_details,
+      ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+}
+
 void QuicSession::SendMaxStreams(QuicStreamCount stream_count,
                                  bool unidirectional) {
   control_frame_manager_.WriteOrBufferMaxStreams(stream_count, unidirectional);
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index 138f6c1..390dc73 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -28,6 +28,7 @@
 #include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h"
 #include "net/third_party/quiche/src/quic/core/session_notifier_interface.h"
+#include "net/third_party/quiche/src/quic/core/stream_delegate_interface.h"
 #include "net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
@@ -50,7 +51,8 @@
       public SessionNotifierInterface,
       public QuicStreamFrameDataProducer,
       public QuicStreamIdManager::DelegateInterface,
-      public HandshakerDelegateInterface {
+      public HandshakerDelegateInterface,
+      public StreamDelegateInterface {
  public:
   // An interface from the session to the entity owning the session.
   // This lets the session notify its owner (the Dispatcher) when the connection
@@ -256,6 +258,10 @@
   void NeuterUnencryptedData() override;
   void NeuterHandshakeData() override;
 
+  // Implement StreamDelegateInterface.
+  void OnStreamError(QuicErrorCode error_code,
+                     std::string error_details) override;
+
   // Called by the QuicCryptoStream when a handshake message is sent.
   virtual void OnCryptoHandshakeMessageSent(
       const CryptoHandshakeMessage& message);
diff --git a/quic/core/quic_stream.cc b/quic/core/quic_stream.cc
index f2c7c88..9d8407a 100644
--- a/quic/core/quic_stream.cc
+++ b/quic/core/quic_stream.cc
@@ -112,6 +112,7 @@
 PendingStream::PendingStream(QuicStreamId id, QuicSession* session)
     : id_(id),
       session_(session),
+      stream_delegate_(session),
       stream_bytes_read_(0),
       fin_received_(false),
       connection_flow_controller_(session->flow_controller()),
@@ -151,8 +152,7 @@
 
 void PendingStream::CloseConnectionWithDetails(QuicErrorCode error,
                                                const std::string& details) {
-  session_->connection()->CloseConnection(
-      error, details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+  stream_delegate_->OnStreamError(error, details);
 }
 
 QuicStreamId PendingStream::id() const {
@@ -337,6 +337,7 @@
     : sequencer_(std::move(sequencer)),
       id_(id),
       session_(session),
+      stream_delegate_(session),
       precedence_(CalculateDefaultPriority(session)),
       stream_bytes_read_(stream_bytes_read),
       stream_error_(QUIC_STREAM_NO_ERROR),
@@ -550,8 +551,7 @@
 
 void QuicStream::CloseConnectionWithDetails(QuicErrorCode error,
                                             const std::string& details) {
-  session()->connection()->CloseConnection(
-      error, details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+  stream_delegate_->OnStreamError(error, details);
 }
 
 const spdy::SpdyStreamPrecedence& QuicStream::precedence() const {
diff --git a/quic/core/quic_stream.h b/quic/core/quic_stream.h
index 0688641..1d05292 100644
--- a/quic/core/quic_stream.h
+++ b/quic/core/quic_stream.h
@@ -28,6 +28,7 @@
 #include "net/third_party/quiche/src/quic/core/quic_stream_sequencer.h"
 #include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/core/session_notifier_interface.h"
+#include "net/third_party/quiche/src/quic/core/stream_delegate_interface.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_optional.h"
@@ -91,7 +92,9 @@
   QuicStreamId id_;
 
   // Session which owns this.
+  // TODO(b/136274541): Remove session pointer from streams.
   QuicSession* session_;
+  StreamDelegateInterface* stream_delegate_;
 
   // Bytes read refers to payload bytes only: they do not include framing,
   // encryption overhead etc.
@@ -400,6 +403,8 @@
   // this virtual so that subclasses can implement their own logics.
   virtual void OnDeadlinePassed();
 
+  StreamDelegateInterface* stream_delegate() { return stream_delegate_; }
+
   bool fin_buffered() const { return fin_buffered_; }
 
   const QuicSession* session() const { return session_; }
@@ -451,7 +456,9 @@
   QuicStreamSequencer sequencer_;
   QuicStreamId id_;
   // Pointer to the owning QuicSession object.
+  // TODO(b/136274541): Remove session pointer from streams.
   QuicSession* session_;
+  StreamDelegateInterface* stream_delegate_;
   // The precedence of the stream, once parsed.
   spdy::SpdyStreamPrecedence precedence_;
   // Bytes read refers to payload bytes only: they do not include framing,
diff --git a/quic/core/quic_stream_sequencer.h b/quic/core/quic_stream_sequencer.h
index 68845b8..0733664 100644
--- a/quic/core/quic_stream_sequencer.h
+++ b/quic/core/quic_stream_sequencer.h
@@ -35,7 +35,7 @@
     virtual void OnFinRead() = 0;
     // Called when bytes have been consumed from the sequencer.
     virtual void AddBytesConsumed(QuicByteCount bytes) = 0;
-    // TODO(rch): Clean up this interface via OnUnrecoverableError and
+    // TODO(b/136274541): Clean up this interface via OnUnrecoverableError and
     // remove PeerAddressOfLatestPacket().
     // Called when an error has occurred which should result in the stream
     // being reset.
diff --git a/quic/core/stream_delegate_interface.h b/quic/core/stream_delegate_interface.h
new file mode 100644
index 0000000..f6d052a
--- /dev/null
+++ b/quic/core/stream_delegate_interface.h
@@ -0,0 +1,23 @@
+// 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_CORE_STREAM_DELEGATE_INTERFACE_H_
+#define QUICHE_QUIC_CORE_STREAM_DELEGATE_INTERFACE_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE StreamDelegateInterface {
+ public:
+  virtual ~StreamDelegateInterface() {}
+
+  // Called when the stream has encountered errors that it can't handle.
+  virtual void OnStreamError(QuicErrorCode error_code,
+                             std::string error_details) = 0;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_CORE_STREAM_DELEGATE_INTERFACE_H_