Implement pause/resume in HttpDecoder.

Previously if an HttpDecoder::Visitor method returned false, HttpDecoder aborted
processing without the possibility to resume.  This CL makes HttpDecoder keep
its internal state ready for further processing.  This is in preparation for
blocked QPACK decoding.

gfe-relnote: n/a, change in QUIC v99-only class.
PiperOrigin-RevId: 255086707
Change-Id: I263fef846d2042a9169e859de4b7c72ccfd9c45b
diff --git a/quic/core/http/http_decoder.cc b/quic/core/http/http_decoder.cc
index 19aec11..1bcbfaa 100644
--- a/quic/core/http/http_decoder.cc
+++ b/quic/core/http/http_decoder.cc
@@ -46,20 +46,21 @@
 
 QuicByteCount HttpDecoder::ProcessInput(const char* data, QuicByteCount len) {
   QuicDataReader reader(data, len);
-  while (error_ == QUIC_NO_ERROR &&
+  bool continue_processing = true;
+  while (continue_processing && error_ == QUIC_NO_ERROR &&
          (reader.BytesRemaining() != 0 || state_ == STATE_FINISH_PARSING)) {
     switch (state_) {
       case STATE_READING_FRAME_TYPE:
         ReadFrameType(&reader);
         break;
       case STATE_READING_FRAME_LENGTH:
-        ReadFrameLength(&reader);
+        continue_processing = ReadFrameLength(&reader);
         break;
       case STATE_READING_FRAME_PAYLOAD:
-        ReadFramePayload(&reader);
+        continue_processing = ReadFramePayload(&reader);
         break;
       case STATE_FINISH_PARSING:
-        FinishParsing();
+        continue_processing = FinishParsing();
         break;
       case STATE_ERROR:
         break;
@@ -68,10 +69,6 @@
     }
   }
 
-  if (error_ != QUIC_NO_ERROR) {
-    return 0;
-  }
-
   return len - reader.BytesRemaining();
 }
 
@@ -105,7 +102,7 @@
   state_ = STATE_READING_FRAME_LENGTH;
 }
 
-void HttpDecoder::ReadFrameLength(QuicDataReader* reader) {
+bool HttpDecoder::ReadFrameLength(QuicDataReader* reader) {
   DCHECK_NE(0u, reader->BytesRemaining());
   if (current_length_field_length_ == 0) {
     // A new frame is coming.
@@ -115,7 +112,7 @@
       // Buffer a new length field.
       remaining_length_field_length_ = current_length_field_length_;
       BufferFrameLength(reader);
-      return;
+      return true;
     }
     // The reader has all length data needed, so no need to buffer.
     bool success = reader->ReadVarInt62(&current_frame_length_);
@@ -125,7 +122,7 @@
     BufferFrameLength(reader);
     // The frame is still not buffered completely.
     if (remaining_length_field_length_ != 0) {
-      return;
+      return true;
     }
     QuicDataReader length_reader(length_buffer_.data(),
                                  current_length_field_length_);
@@ -137,47 +134,37 @@
     // TODO(bnc): Signal HTTP_EXCESSIVE_LOAD or similar to peer.
     RaiseError(QUIC_INTERNAL_ERROR, "Frame is too large");
     visitor_->OnError(this);
-    return;
+    return false;
   }
 
   // Calling the following visitor methods does not require parsing of any
   // frame payload.
+  bool continue_processing = true;
   auto frame_meta = Http3FrameLengths(
       current_length_field_length_ + current_type_field_length_,
       current_frame_length_);
   if (current_frame_type_ == 0x0) {
-    if (!visitor_->OnDataFrameStart(frame_meta)) {
-      RaiseError(QUIC_INTERNAL_ERROR, "Visitor shut down on DATA frame start.");
-      return;
-    }
+    continue_processing = visitor_->OnDataFrameStart(frame_meta);
   } else if (current_frame_type_ == 0x1) {
-    if (!visitor_->OnHeadersFrameStart(frame_meta)) {
-      RaiseError(QUIC_INTERNAL_ERROR,
-                 "Visitor shut down on HEADERS frame start.");
-      return;
-    }
+    continue_processing = visitor_->OnHeadersFrameStart(frame_meta);
   } else if (current_frame_type_ == 0x4) {
-    if (!visitor_->OnSettingsFrameStart(frame_meta)) {
-      RaiseError(QUIC_INTERNAL_ERROR,
-                 "Visitor shut down on SETTINGS frame start.");
-      return;
-    }
+    continue_processing = visitor_->OnSettingsFrameStart(frame_meta);
   } else if (current_frame_type_ == 0x2) {
-    if (!visitor_->OnPriorityFrameStart(frame_meta)) {
-      RaiseError(QUIC_INTERNAL_ERROR,
-                 "Visitor shut down on PRIORITY frame start.");
-      return;
-    }
+    continue_processing = visitor_->OnPriorityFrameStart(frame_meta);
   }
 
   remaining_frame_length_ = current_frame_length_;
   state_ = (remaining_frame_length_ == 0) ? STATE_FINISH_PARSING
                                           : STATE_READING_FRAME_PAYLOAD;
+  return continue_processing;
 }
 
-void HttpDecoder::ReadFramePayload(QuicDataReader* reader) {
+bool HttpDecoder::ReadFramePayload(QuicDataReader* reader) {
   DCHECK_NE(0u, reader->BytesRemaining());
   DCHECK_NE(0u, remaining_frame_length_);
+
+  bool continue_processing = true;
+
   switch (current_frame_type_) {
     case 0x0: {  // DATA
       QuicByteCount bytes_to_read = std::min<QuicByteCount>(
@@ -186,10 +173,7 @@
       bool success = reader->ReadStringPiece(&payload, bytes_to_read);
       DCHECK(success);
       DCHECK(!payload.empty());
-      if (!visitor_->OnDataFramePayload(payload)) {
-        RaiseError(QUIC_INTERNAL_ERROR, "Visitor shut down on DATA frame.");
-        return;
-      }
+      continue_processing = visitor_->OnDataFramePayload(payload);
       remaining_frame_length_ -= payload.length();
       break;
     }
@@ -200,10 +184,7 @@
       bool success = reader->ReadStringPiece(&payload, bytes_to_read);
       DCHECK(success);
       DCHECK(!payload.empty());
-      if (!visitor_->OnHeadersFramePayload(payload)) {
-        RaiseError(QUIC_INTERNAL_ERROR, "Visitor shut down on HEADERS frame.");
-        return;
-      }
+      continue_processing = visitor_->OnHeadersFramePayload(payload);
       remaining_frame_length_ -= payload.length();
       break;
     }
@@ -228,13 +209,12 @@
         // TODO(rch): Handle partial delivery of this field.
         if (!reader->ReadVarInt62(&push_id)) {
           RaiseError(QUIC_INTERNAL_ERROR, "Unable to read push_id");
-          return;
+          return false;
         }
         remaining_frame_length_ -= bytes_remaining - reader->BytesRemaining();
         if (!visitor_->OnPushPromiseFrameStart(push_id)) {
-          RaiseError(QUIC_INTERNAL_ERROR,
-                     "Visitor shut down on PUSH_PROMISE frame start.");
-          return;
+          continue_processing = false;
+          break;
         }
       }
       DCHECK_LT(remaining_frame_length_, current_frame_length_);
@@ -247,11 +227,7 @@
       bool success = reader->ReadStringPiece(&payload, bytes_to_read);
       DCHECK(success);
       DCHECK(!payload.empty());
-      if (!visitor_->OnPushPromiseFramePayload(payload)) {
-        RaiseError(QUIC_INTERNAL_ERROR,
-                   "Visitor shut down on PUSH_PROMISE frame.");
-        return;
-      }
+      continue_processing = visitor_->OnPushPromiseFramePayload(payload);
       remaining_frame_length_ -= payload.length();
       break;
     }
@@ -291,30 +267,28 @@
       QUIC_FALLTHROUGH_INTENDED;
     default:
       DiscardFramePayload(reader);
-      return;
+      return true;
   }
 
   if (remaining_frame_length_ == 0) {
     state_ = STATE_FINISH_PARSING;
   }
+
+  return continue_processing;
 }
 
-void HttpDecoder::FinishParsing() {
+bool HttpDecoder::FinishParsing() {
   DCHECK_EQ(0u, remaining_frame_length_);
+
+  bool continue_processing = true;
+
   switch (current_frame_type_) {
     case 0x0: {  // DATA
-      if (!visitor_->OnDataFrameEnd()) {
-        RaiseError(QUIC_INTERNAL_ERROR, "Visitor shut down on DATA frame end.");
-        return;
-      }
+      continue_processing = visitor_->OnDataFrameEnd();
       break;
     }
     case 0x1: {  // HEADERS
-      if (!visitor_->OnHeadersFrameEnd()) {
-        RaiseError(QUIC_INTERNAL_ERROR,
-                   "Visitor shut down on HEADERS frame end.");
-        return;
-      }
+      continue_processing = visitor_->OnHeadersFrameEnd();
       break;
     }
     case 0x2: {  // PRIORITY
@@ -323,12 +297,9 @@
       PriorityFrame frame;
       QuicDataReader reader(buffer_.data(), current_frame_length_);
       if (!ParsePriorityFrame(&reader, &frame)) {
-        return;
+        return false;
       }
-      if (!visitor_->OnPriorityFrame(frame)) {
-        RaiseError(QUIC_INTERNAL_ERROR, "Visitor shut down on PRIORITY frame.");
-        return;
-      }
+      continue_processing = visitor_->OnPriorityFrame(frame);
       break;
     }
     case 0x3: {  // CANCEL_PUSH
@@ -337,33 +308,22 @@
       QuicDataReader reader(buffer_.data(), current_frame_length_);
       if (!reader.ReadVarInt62(&frame.push_id)) {
         RaiseError(QUIC_INTERNAL_ERROR, "Unable to read push_id");
-        return;
+        return false;
       }
-      if (!visitor_->OnCancelPushFrame(frame)) {
-        RaiseError(QUIC_INTERNAL_ERROR,
-                   "Visitor shut down on CANCEL_PUSH frame.");
-        return;
-      }
+      continue_processing = visitor_->OnCancelPushFrame(frame);
       break;
     }
     case 0x4: {  // SETTINGS
       SettingsFrame frame;
       QuicDataReader reader(buffer_.data(), current_frame_length_);
       if (!ParseSettingsFrame(&reader, &frame)) {
-        return;
+        return false;
       }
-      if (!visitor_->OnSettingsFrame(frame)) {
-        RaiseError(QUIC_INTERNAL_ERROR, "Visitor shut down on SETTINGS frame.");
-        return;
-      }
+      continue_processing = visitor_->OnSettingsFrame(frame);
       break;
     }
     case 0x5: {  // PUSH_PROMISE
-      if (!visitor_->OnPushPromiseFrameEnd()) {
-        RaiseError(QUIC_INTERNAL_ERROR,
-                   "Visitor shut down on PUSH_PROMISE frame end.");
-        return;
-      }
+      continue_processing = visitor_->OnPushPromiseFrameEnd();
       break;
     }
     case 0x7: {  // GOAWAY
@@ -377,13 +337,10 @@
       uint64_t stream_id;
       if (!reader.ReadVarInt62(&stream_id)) {
         RaiseError(QUIC_INTERNAL_ERROR, "Unable to read GOAWAY stream_id");
-        return;
+        return false;
       }
       frame.stream_id = static_cast<QuicStreamId>(stream_id);
-      if (!visitor_->OnGoAwayFrame(frame)) {
-        RaiseError(QUIC_INTERNAL_ERROR, "Visitor shut down on GOAWAY frame.");
-        return;
-      }
+      continue_processing = visitor_->OnGoAwayFrame(frame);
       break;
     }
 
@@ -393,13 +350,9 @@
       MaxPushIdFrame frame;
       if (!reader.ReadVarInt62(&frame.push_id)) {
         RaiseError(QUIC_INTERNAL_ERROR, "Unable to read push_id");
-        return;
+        return false;
       }
-      if (!visitor_->OnMaxPushIdFrame(frame)) {
-        RaiseError(QUIC_INTERNAL_ERROR,
-                   "Visitor shut down on MAX_PUSH_ID frame.");
-        return;
-      }
+      continue_processing = visitor_->OnMaxPushIdFrame(frame);
       break;
     }
 
@@ -409,13 +362,9 @@
       DuplicatePushFrame frame;
       if (!reader.ReadVarInt62(&frame.push_id)) {
         RaiseError(QUIC_INTERNAL_ERROR, "Unable to read push_id");
-        return;
+        return false;
       }
-      if (!visitor_->OnDuplicatePushFrame(frame)) {
-        RaiseError(QUIC_INTERNAL_ERROR,
-                   "Visitor shut down on DUPLICATE_PUSH frame.");
-        return;
-      }
+      continue_processing = visitor_->OnDuplicatePushFrame(frame);
       break;
     }
   }
@@ -423,6 +372,7 @@
   current_length_field_length_ = 0;
   current_type_field_length_ = 0;
   state_ = STATE_READING_FRAME_TYPE;
+  return continue_processing;
 }
 
 void HttpDecoder::DiscardFramePayload(QuicDataReader* reader) {
diff --git a/quic/core/http/http_decoder.h b/quic/core/http/http_decoder.h
index e8332d7..fa0a27b 100644
--- a/quic/core/http/http_decoder.h
+++ b/quic/core/http/http_decoder.h
@@ -47,76 +47,62 @@
     // Called if an error is detected.
     virtual void OnError(HttpDecoder* decoder) = 0;
 
+    // All the following methods return true to continue decoding,
+    // and false to pause it.
+
     // Called when a PRIORITY frame has been received.
     // |frame_length| contains PRIORITY frame length and payload length.
-    // Returns true to permit furthuring decoding, and false to prevent it.
     virtual bool OnPriorityFrameStart(Http3FrameLengths frame_length) = 0;
 
     // Called when a PRIORITY frame has been successfully parsed.
-    // Returns true to permit furthuring decoding, and false to prevent it.
     virtual bool OnPriorityFrame(const PriorityFrame& frame) = 0;
 
     // Called when a CANCEL_PUSH frame has been successfully parsed.
-    // Returns true to permit furthuring decoding, and false to prevent it.
     virtual bool OnCancelPushFrame(const CancelPushFrame& frame) = 0;
 
     // Called when a MAX_PUSH_ID frame has been successfully parsed.
-    // Returns true to permit furthuring decoding, and false to prevent it.
     virtual bool OnMaxPushIdFrame(const MaxPushIdFrame& frame) = 0;
 
     // Called when a GOAWAY frame has been successfully parsed.
-    // Returns true to permit furthuring decoding, and false to prevent it.
     virtual bool OnGoAwayFrame(const GoAwayFrame& frame) = 0;
 
     // Called when a SETTINGS frame has been received.
-    // Returns true to permit furthuring decoding, and false to prevent it.
     virtual bool OnSettingsFrameStart(Http3FrameLengths frame_length) = 0;
 
     // Called when a SETTINGS frame has been successfully parsed.
-    // Returns true to permit furthuring decoding, and false to prevent it.
     virtual bool OnSettingsFrame(const SettingsFrame& frame) = 0;
 
     // Called when a DUPLICATE_PUSH frame has been successfully parsed.
-    // Returns true to permit furthuring decoding, and false to prevent it.
     virtual bool OnDuplicatePushFrame(const DuplicatePushFrame& frame) = 0;
 
     // Called when a DATA frame has been received.
     // |frame_length| contains DATA frame length and payload length.
-    // Returns true to permit furthuring decoding, and false to prevent it.
     virtual bool OnDataFrameStart(Http3FrameLengths frame_length) = 0;
     // Called when part of the payload of a DATA frame has been read.  May be
     // called multiple times for a single frame.  |payload| is guaranteed to be
     // non-empty.
-    // Returns true to permit furthuring decoding, and false to prevent it.
     virtual bool OnDataFramePayload(QuicStringPiece payload) = 0;
     // Called when a DATA frame has been completely processed.
-    // Returns true to permit furthuring decoding, and false to prevent it.
     virtual bool OnDataFrameEnd() = 0;
 
     // Called when a HEADERS frame has been received.
     // |frame_length| contains HEADERS frame length and payload length.
-    // Returns true to permit furthuring decoding, and false to prevent it.
     virtual bool OnHeadersFrameStart(Http3FrameLengths frame_length) = 0;
     // Called when part of the payload of a HEADERS frame has been read.  May be
     // called multiple times for a single frame.  |payload| is guaranteed to be
     // non-empty.
-    // Returns true to permit furthuring decoding, and false to prevent it.
     virtual bool OnHeadersFramePayload(QuicStringPiece payload) = 0;
     // Called when a HEADERS frame has been completely processed.
     // |frame_len| is the length of the HEADERS frame payload.
-    // Returns true to permit furthuring decoding, and false to prevent it.
     virtual bool OnHeadersFrameEnd() = 0;
 
     // Called when a PUSH_PROMISE frame has been received for |push_id|.
-    // Returns true to permit furthuring decoding, and false to prevent it.
     virtual bool OnPushPromiseFrameStart(PushId push_id) = 0;
     // Called when part of the payload of a PUSH_PROMISE frame has been read.
     // May be called multiple times for a single frame.  |payload| is guaranteed
     // to be non-empty.
-    // Returns true to permit furthuring decoding, and false to prevent it.
     virtual bool OnPushPromiseFramePayload(QuicStringPiece payload) = 0;
     // Called when a PUSH_PROMISE frame has been completely processed.
-    // Returns true to permit furthuring decoding, and false to prevent it.
     virtual bool OnPushPromiseFrameEnd() = 0;
 
     // TODO(rch): Consider adding methods like:
@@ -134,9 +120,11 @@
   // will be used.  |visitor| will be owned by the caller.
   void set_visitor(Visitor* visitor) { visitor_ = visitor; }
 
-  // Processes the input and invokes the visitor for any frames.
-  // Returns the number of bytes consumed, or 0 if there was an error, in which
-  // case error() should be consulted.
+  // Processes the input and invokes the appropriate visitor methods, until a
+  // visitor method returns false or an error occurs.  Returns the number of
+  // bytes processed.  Does not process any input if called after an error.
+  // Paused processing can be resumed by calling ProcessInput() again with the
+  // unprocessed portion of data.
   QuicByteCount ProcessInput(const char* data, QuicByteCount len);
 
   QuicErrorCode error() const { return error_; }
@@ -160,16 +148,17 @@
   void ReadFrameType(QuicDataReader* reader);
 
   // Reads the length of a frame from |reader|. Sets error_ and error_detail_
-  // if there are any errors.
-  void ReadFrameLength(QuicDataReader* reader);
+  // if there are any errors.  Returns whether processing should continue.
+  bool ReadFrameLength(QuicDataReader* reader);
 
   // Reads the payload of the current frame from |reader| and processes it,
-  // possibly buffering the data or invoking the visitor.
-  void ReadFramePayload(QuicDataReader* reader);
+  // possibly buffering the data or invoking the visitor.  Returns whether
+  // processing should continue.
+  bool ReadFramePayload(QuicDataReader* reader);
 
   // Optionally parses buffered data; calls visitor method to signal that frame
-  // had been parsed completely.
-  void FinishParsing();
+  // had been parsed completely.  Returns whether processing should continue.
+  bool FinishParsing();
 
   // Discards any remaining frame payload from |reader|.
   void DiscardFramePayload(QuicDataReader* reader);
diff --git a/quic/core/http/http_decoder_test.cc b/quic/core/http/http_decoder_test.cc
index eca52ce..135271b 100644
--- a/quic/core/http/http_decoder_test.cc
+++ b/quic/core/http/http_decoder_test.cc
@@ -8,6 +8,7 @@
 #include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
 
 using ::testing::_;
@@ -82,6 +83,7 @@
     return HttpDecoderPeer::current_frame_type(&decoder_);
   }
 
+  // Process |input| in a single call to HttpDecoder::ProcessInput().
   QuicByteCount ProcessInput(QuicStringPiece input) {
     return decoder_.ProcessInput(input.data(), input.size());
   }
@@ -94,6 +96,22 @@
     }
   }
 
+  // Append garbage to |input|, then process it in a single call to
+  // HttpDecoder::ProcessInput().  Verify that garbage is not read.
+  QuicByteCount ProcessInputWithGarbageAppended(QuicStringPiece input) {
+    std::string input_with_garbage_appended = QuicStrCat(input, "blahblah");
+    QuicByteCount processed_bytes = ProcessInput(input_with_garbage_appended);
+
+    // Guaranteed by HttpDecoder::ProcessInput() contract.
+    DCHECK_LE(processed_bytes, input_with_garbage_appended.size());
+
+    // Caller should set up visitor to pause decoding
+    // before HttpDecoder would read garbage.
+    EXPECT_LE(processed_bytes, input.size());
+
+    return processed_bytes;
+  }
+
   HttpDecoder decoder_;
   testing::StrictMock<MockVisitor> visitor_;
 };
@@ -200,11 +218,19 @@
 }
 
 TEST_F(HttpDecoderTest, CancelPush) {
+  InSequence s;
   std::string input =
       "\x03"   // type (CANCEL_PUSH)
       "\x01"   // length
       "\x01";  // Push Id
 
+  // Visitor pauses processing.
+  EXPECT_CALL(visitor_, OnCancelPushFrame(CancelPushFrame({1})))
+      .WillOnce(Return(false));
+  EXPECT_EQ(input.size(), ProcessInputWithGarbageAppended(input));
+  EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+  EXPECT_EQ("", decoder_.error_detail());
+
   // Process the full frame.
   EXPECT_CALL(visitor_, OnCancelPushFrame(CancelPushFrame({1})));
   EXPECT_EQ(input.size(), ProcessInput(input));
@@ -216,24 +242,35 @@
   ProcessInputCharByChar(input);
   EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
   EXPECT_EQ("", decoder_.error_detail());
-
-  // Test on the situation when the visitor wants to stop processing.
-  EXPECT_CALL(visitor_, OnCancelPushFrame(CancelPushFrame({1})))
-      .WillOnce(Return(false));
-  EXPECT_EQ(0u, ProcessInput(input));
-  EXPECT_EQ(QUIC_INTERNAL_ERROR, decoder_.error());
-  EXPECT_EQ("Visitor shut down on CANCEL_PUSH frame.", decoder_.error_detail());
 }
 
 TEST_F(HttpDecoderTest, PushPromiseFrame) {
+  InSequence s;
   std::string input =
       "\x05"      // type (PUSH_PROMISE)
       "\x08"      // length
       "\x01"      // Push Id
       "Headers";  // Header Block
 
+  // Visitor pauses processing.
+  EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1)).WillOnce(Return(false));
+  QuicStringPiece remaining_input(input);
+  QuicByteCount processed_bytes =
+      ProcessInputWithGarbageAppended(remaining_input);
+  EXPECT_EQ(3u, processed_bytes);
+  remaining_input = remaining_input.substr(processed_bytes);
+
+  EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("Headers")))
+      .WillOnce(Return(false));
+  processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
+  EXPECT_EQ(remaining_input.size(), processed_bytes);
+
+  EXPECT_CALL(visitor_, OnPushPromiseFrameEnd()).WillOnce(Return(false));
+  EXPECT_EQ(0u, ProcessInputWithGarbageAppended(""));
+  EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+  EXPECT_EQ("", decoder_.error_detail());
+
   // Process the full frame.
-  InSequence s;
   EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1));
   EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("Headers")));
   EXPECT_CALL(visitor_, OnPushPromiseFrameEnd());
@@ -254,21 +291,22 @@
   ProcessInputCharByChar(input);
   EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
   EXPECT_EQ("", decoder_.error_detail());
-
-  // Test on the situation when the visitor wants to stop processing.
-  EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1)).WillOnce(Return(false));
-  EXPECT_EQ(0u, ProcessInput(input));
-  EXPECT_EQ(QUIC_INTERNAL_ERROR, decoder_.error());
-  EXPECT_EQ("Visitor shut down on PUSH_PROMISE frame start.",
-            decoder_.error_detail());
 }
 
 TEST_F(HttpDecoderTest, MaxPushId) {
+  InSequence s;
   std::string input =
       "\x0D"   // type (MAX_PUSH_ID)
       "\x01"   // length
       "\x01";  // Push Id
 
+  // Visitor pauses processing.
+  EXPECT_CALL(visitor_, OnMaxPushIdFrame(MaxPushIdFrame({1})))
+      .WillOnce(Return(false));
+  EXPECT_EQ(input.size(), ProcessInputWithGarbageAppended(input));
+  EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+  EXPECT_EQ("", decoder_.error_detail());
+
   // Process the full frame.
   EXPECT_CALL(visitor_, OnMaxPushIdFrame(MaxPushIdFrame({1})));
   EXPECT_EQ(input.size(), ProcessInput(input));
@@ -280,20 +318,22 @@
   ProcessInputCharByChar(input);
   EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
   EXPECT_EQ("", decoder_.error_detail());
-
-  // Test on the situation when the visitor wants to stop processing.
-  EXPECT_CALL(visitor_, OnMaxPushIdFrame(MaxPushIdFrame({1})))
-      .WillOnce(Return(false));
-  EXPECT_EQ(0u, ProcessInput(input));
-  EXPECT_EQ(QUIC_INTERNAL_ERROR, decoder_.error());
-  EXPECT_EQ("Visitor shut down on MAX_PUSH_ID frame.", decoder_.error_detail());
 }
 
 TEST_F(HttpDecoderTest, DuplicatePush) {
+  InSequence s;
   std::string input =
       "\x0E"   // type (DUPLICATE_PUSH)
       "\x01"   // length
       "\x01";  // Push Id
+
+  // Visitor pauses processing.
+  EXPECT_CALL(visitor_, OnDuplicatePushFrame(DuplicatePushFrame({1})))
+      .WillOnce(Return(false));
+  EXPECT_EQ(input.size(), ProcessInputWithGarbageAppended(input));
+  EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+  EXPECT_EQ("", decoder_.error_detail());
+
   // Process the full frame.
   EXPECT_CALL(visitor_, OnDuplicatePushFrame(DuplicatePushFrame({1})));
   EXPECT_EQ(input.size(), ProcessInput(input));
@@ -305,17 +345,10 @@
   ProcessInputCharByChar(input);
   EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
   EXPECT_EQ("", decoder_.error_detail());
-
-  // Test on the situation when the visitor wants to stop processing.
-  EXPECT_CALL(visitor_, OnDuplicatePushFrame(DuplicatePushFrame({1})))
-      .WillOnce(Return(false));
-  EXPECT_EQ(0u, ProcessInput(input));
-  EXPECT_EQ(QUIC_INTERNAL_ERROR, decoder_.error());
-  EXPECT_EQ("Visitor shut down on DUPLICATE_PUSH frame.",
-            decoder_.error_detail());
 }
 
 TEST_F(HttpDecoderTest, PriorityFrame) {
+  InSequence s;
   std::string input =
       "\x02"   // type (PRIORITY)
       "\x04"   // length
@@ -332,6 +365,21 @@
   frame.element_dependency_id = 0x04;
   frame.weight = 0xFF;
 
+  // Visitor pauses processing.
+  EXPECT_CALL(visitor_, OnPriorityFrameStart(Http3FrameLengths(2, 4)))
+      .WillOnce(Return(false));
+  QuicStringPiece remaining_input(input);
+  QuicByteCount processed_bytes =
+      ProcessInputWithGarbageAppended(remaining_input);
+  EXPECT_EQ(2u, processed_bytes);
+  remaining_input = remaining_input.substr(processed_bytes);
+
+  EXPECT_CALL(visitor_, OnPriorityFrame(frame)).WillOnce(Return(false));
+  processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
+  EXPECT_EQ(remaining_input.size(), processed_bytes);
+  EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+  EXPECT_EQ("", decoder_.error_detail());
+
   // Process the full frame.
   EXPECT_CALL(visitor_, OnPriorityFrameStart(Http3FrameLengths(2, 4)));
   EXPECT_CALL(visitor_, OnPriorityFrame(frame));
@@ -362,16 +410,10 @@
   EXPECT_EQ(input2.size(), ProcessInput(input2));
   EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
   EXPECT_EQ("", decoder_.error_detail());
-
-  // Test on the situation when the visitor wants to stop processing.
-  EXPECT_CALL(visitor_, OnPriorityFrameStart(Http3FrameLengths(2, 4)));
-  EXPECT_CALL(visitor_, OnPriorityFrame(frame)).WillOnce(Return(false));
-  EXPECT_EQ(0u, ProcessInput(input));
-  EXPECT_EQ(QUIC_INTERNAL_ERROR, decoder_.error());
-  EXPECT_EQ("Visitor shut down on PRIORITY frame.", decoder_.error_detail());
 }
 
 TEST_F(HttpDecoderTest, SettingsFrame) {
+  InSequence s;
   std::string input(
       "\x04"      // type (SETTINGS)
       "\x07"      // length
@@ -388,6 +430,21 @@
   frame.values[6] = 5;
   frame.values[256] = 4;
 
+  // Visitor pauses processing.
+  QuicStringPiece remaining_input(input);
+  EXPECT_CALL(visitor_, OnSettingsFrameStart(Http3FrameLengths(2, 7)))
+      .WillOnce(Return(false));
+  QuicByteCount processed_bytes =
+      ProcessInputWithGarbageAppended(remaining_input);
+  EXPECT_EQ(2u, processed_bytes);
+  remaining_input = remaining_input.substr(processed_bytes);
+
+  EXPECT_CALL(visitor_, OnSettingsFrame(frame)).WillOnce(Return(false));
+  processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
+  EXPECT_EQ(remaining_input.size(), processed_bytes);
+  EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+  EXPECT_EQ("", decoder_.error_detail());
+
   // Process the full frame.
   EXPECT_CALL(visitor_, OnSettingsFrameStart(Http3FrameLengths(2, 7)));
   EXPECT_CALL(visitor_, OnSettingsFrame(frame));
@@ -401,25 +458,36 @@
   ProcessInputCharByChar(input);
   EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
   EXPECT_EQ("", decoder_.error_detail());
-
-  // Test on the situation when the visitor wants to stop processing.
-  EXPECT_CALL(visitor_, OnSettingsFrameStart(Http3FrameLengths(2, 7)))
-      .WillOnce(Return(false));
-  EXPECT_EQ(0u, ProcessInput(input));
-  EXPECT_EQ(QUIC_INTERNAL_ERROR, decoder_.error());
-  EXPECT_EQ("Visitor shut down on SETTINGS frame start.",
-            decoder_.error_detail());
 }
 
 TEST_F(HttpDecoderTest, DataFrame) {
+  InSequence s;
   std::string input(
       "\x00"    // type (DATA)
       "\x05"    // length
       "Data!",  // data
       7);
 
+  // Visitor pauses processing.
+  EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(2, 5)))
+      .WillOnce(Return(false));
+  QuicStringPiece remaining_input(input);
+  QuicByteCount processed_bytes =
+      ProcessInputWithGarbageAppended(remaining_input);
+  EXPECT_EQ(2u, processed_bytes);
+  remaining_input = remaining_input.substr(processed_bytes);
+
+  EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("Data!")))
+      .WillOnce(Return(false));
+  processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
+  EXPECT_EQ(remaining_input.size(), processed_bytes);
+
+  EXPECT_CALL(visitor_, OnDataFrameEnd()).WillOnce(Return(false));
+  EXPECT_EQ(0u, ProcessInputWithGarbageAppended(""));
+  EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+  EXPECT_EQ("", decoder_.error_detail());
+
   // Process the full frame.
-  InSequence s;
   EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(2, 5)));
   EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("Data!")));
   EXPECT_CALL(visitor_, OnDataFrameEnd());
@@ -438,16 +506,10 @@
   ProcessInputCharByChar(input);
   EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
   EXPECT_EQ("", decoder_.error_detail());
-
-  // Test on the situation when the visitor wants to stop processing.
-  EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(2, 5)))
-      .WillOnce(Return(false));
-  EXPECT_EQ(0u, ProcessInput(input));
-  EXPECT_EQ(QUIC_INTERNAL_ERROR, decoder_.error());
-  EXPECT_EQ("Visitor shut down on DATA frame start.", decoder_.error_detail());
 }
 
 TEST_F(HttpDecoderTest, FrameHeaderPartialDelivery) {
+  InSequence s;
   // A large input that will occupy more than 1 byte in the length field.
   std::string input(2048, 'x');
   HttpEncoder encoder;
@@ -497,11 +559,19 @@
 }
 
 TEST_F(HttpDecoderTest, GoAway) {
+  InSequence s;
   std::string input =
       "\x07"   // type (GOAWAY)
       "\x01"   // length
       "\x01";  // StreamId
 
+  // Visitor pauses processing.
+  EXPECT_CALL(visitor_, OnGoAwayFrame(GoAwayFrame({1})))
+      .WillOnce(Return(false));
+  EXPECT_EQ(input.size(), ProcessInputWithGarbageAppended(input));
+  EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+  EXPECT_EQ("", decoder_.error_detail());
+
   // Process the full frame.
   EXPECT_CALL(visitor_, OnGoAwayFrame(GoAwayFrame({1})));
   EXPECT_EQ(input.size(), ProcessInput(input));
@@ -516,13 +586,32 @@
 }
 
 TEST_F(HttpDecoderTest, HeadersFrame) {
+  InSequence s;
   std::string input =
       "\x01"      // type (HEADERS)
       "\x07"      // length
       "Headers";  // headers
 
+  // Visitor pauses processing.
+  EXPECT_CALL(visitor_, OnHeadersFrameStart(Http3FrameLengths(2, 7)))
+      .WillOnce(Return(false));
+  QuicStringPiece remaining_input(input);
+  QuicByteCount processed_bytes =
+      ProcessInputWithGarbageAppended(remaining_input);
+  EXPECT_EQ(2u, processed_bytes);
+  remaining_input = remaining_input.substr(processed_bytes);
+
+  EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("Headers")))
+      .WillOnce(Return(false));
+  processed_bytes = ProcessInputWithGarbageAppended(remaining_input);
+  EXPECT_EQ(remaining_input.size(), processed_bytes);
+
+  EXPECT_CALL(visitor_, OnHeadersFrameEnd()).WillOnce(Return(false));
+  EXPECT_EQ(0u, ProcessInputWithGarbageAppended(""));
+  EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+  EXPECT_EQ("", decoder_.error_detail());
+
   // Process the full frame.
-  InSequence s;
   EXPECT_CALL(visitor_, OnHeadersFrameStart(Http3FrameLengths(2, 7)));
   EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("Headers")));
   EXPECT_CALL(visitor_, OnHeadersFrameEnd());
@@ -546,13 +635,23 @@
 }
 
 TEST_F(HttpDecoderTest, EmptyDataFrame) {
+  InSequence s;
   std::string input(
       "\x00"   // type (DATA)
       "\x00",  // length
       2);
 
+  // Visitor pauses processing.
+  EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(2, 0)))
+      .WillOnce(Return(false));
+  EXPECT_EQ(input.size(), ProcessInputWithGarbageAppended(input));
+
+  EXPECT_CALL(visitor_, OnDataFrameEnd()).WillOnce(Return(false));
+  EXPECT_EQ(0u, ProcessInputWithGarbageAppended(""));
+  EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+  EXPECT_EQ("", decoder_.error_detail());
+
   // Process the full frame.
-  InSequence s;
   EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(2, 0)));
   EXPECT_CALL(visitor_, OnDataFrameEnd());
   EXPECT_EQ(input.size(), ProcessInput(input));
@@ -568,13 +667,23 @@
 }
 
 TEST_F(HttpDecoderTest, EmptyHeadersFrame) {
+  InSequence s;
   std::string input(
       "\x01"   // type (HEADERS)
       "\x00",  // length
       2);
 
+  // Visitor pauses processing.
+  EXPECT_CALL(visitor_, OnHeadersFrameStart(Http3FrameLengths(2, 0)))
+      .WillOnce(Return(false));
+  EXPECT_EQ(input.size(), ProcessInputWithGarbageAppended(input));
+
+  EXPECT_CALL(visitor_, OnHeadersFrameEnd()).WillOnce(Return(false));
+  EXPECT_EQ(0u, ProcessInputWithGarbageAppended(""));
+  EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+  EXPECT_EQ("", decoder_.error_detail());
+
   // Process the full frame.
-  InSequence s;
   EXPECT_CALL(visitor_, OnHeadersFrameStart(Http3FrameLengths(2, 0)));
   EXPECT_CALL(visitor_, OnHeadersFrameEnd());
   EXPECT_EQ(input.size(), ProcessInput(input));
@@ -590,13 +699,22 @@
 }
 
 TEST_F(HttpDecoderTest, PushPromiseFrameNoHeaders) {
+  InSequence s;
   std::string input =
       "\x05"   // type (PUSH_PROMISE)
       "\x01"   // length
       "\x01";  // Push Id
 
+  // Visitor pauses processing.
+  EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1)).WillOnce(Return(false));
+  EXPECT_EQ(input.size(), ProcessInputWithGarbageAppended(input));
+
+  EXPECT_CALL(visitor_, OnPushPromiseFrameEnd()).WillOnce(Return(false));
+  EXPECT_EQ(0u, ProcessInputWithGarbageAppended(""));
+  EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+  EXPECT_EQ("", decoder_.error_detail());
+
   // Process the full frame.
-  InSequence s;
   EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1));
   EXPECT_CALL(visitor_, OnPushPromiseFrameEnd());
   EXPECT_EQ(input.size(), ProcessInput(input));
@@ -618,7 +736,7 @@
       "\x15";  // malformed payload
   // Process the full frame.
   EXPECT_CALL(visitor_, OnError(&decoder_));
-  EXPECT_EQ(0u, ProcessInput(input));
+  EXPECT_EQ(2u, ProcessInput(input));
   EXPECT_EQ(QUIC_INTERNAL_ERROR, decoder_.error());
   EXPECT_EQ("Frame is too large", decoder_.error_detail());
 }
@@ -633,11 +751,44 @@
 
   writer.WriteStringPiece("Malformed payload");
   EXPECT_CALL(visitor_, OnError(&decoder_));
-  EXPECT_EQ(0u, decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input)));
+  EXPECT_EQ(5u, decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input)));
   EXPECT_EQ(QUIC_INTERNAL_ERROR, decoder_.error());
   EXPECT_EQ("Frame is too large", decoder_.error_detail());
 }
 
+TEST_F(HttpDecoderTest, HeadersPausedThenData) {
+  InSequence s;
+  std::string input(
+      "\x01"     // type (HEADERS)
+      "\x07"     // length
+      "Headers"  // headers
+      "\x00"     // type (DATA)
+      "\x05"     // length
+      "Data!",   // data
+      16);
+
+  // Visitor pauses processing, maybe because header decompression is blocked.
+  EXPECT_CALL(visitor_, OnHeadersFrameStart(Http3FrameLengths(2, 7)));
+  EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("Headers")));
+  EXPECT_CALL(visitor_, OnHeadersFrameEnd()).WillOnce(Return(false));
+  QuicStringPiece remaining_input(input);
+  QuicByteCount processed_bytes =
+      ProcessInputWithGarbageAppended(remaining_input);
+  EXPECT_EQ(9u, processed_bytes);
+  remaining_input = remaining_input.substr(processed_bytes);
+
+  // Process DATA frame.
+  EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(2, 5)));
+  EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("Data!")));
+  EXPECT_CALL(visitor_, OnDataFrameEnd());
+
+  processed_bytes = ProcessInput(remaining_input);
+  EXPECT_EQ(remaining_input.size(), processed_bytes);
+
+  EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+  EXPECT_EQ("", decoder_.error_detail());
+}
+
 }  // namespace test
 
 }  // namespace quic