When the GFE issues a 413 error due to excessively long request headers, also send a Clear-Site-Data: "cookies" response header, but only if the portion of the cookie header we have is greater than a certain length.

For full context, see b/181849166.

It ended up being a bit unfortunately gnarly to get access to the incomplete request headers in the HEADERS_TOO_LONG cases, requiring changes to the base BalsaFrame, HTTP/2, and QUIC, but I think the resulting state is not too unreasonable. That said, I'm open to suggestions of a better approach.

Protected by gfe2_restart_flag_clear_long_cookies_on_413.

PiperOrigin-RevId: 547269861
diff --git a/quiche/balsa/balsa_frame.cc b/quiche/balsa/balsa_frame.cc
index 8dd4e97..6990e60 100644
--- a/quiche/balsa/balsa_frame.cc
+++ b/quiche/balsa/balsa_frame.cc
@@ -843,7 +843,7 @@
     // the max_header_length_ (for example after processing the first line)
     // we handle it gracefully.
     if (headers_->GetReadableBytesFromHeaderStream() > max_header_length_) {
-      HandleError(BalsaFrameEnums::HEADERS_TOO_LONG);
+      HandleHeadersTooLongError();
       return message_current - original_message_start;
     }
 
@@ -975,7 +975,7 @@
     // READING_HEADER_AND_FIRSTLINE) in which case we directly declare an error.
     if (header_length > max_header_length_ ||
         (header_length == max_header_length_ && size > 0)) {
-      HandleError(BalsaFrameEnums::HEADERS_TOO_LONG);
+      HandleHeadersTooLongError();
       return current - input;
     }
     const size_t bytes_to_process =
@@ -991,7 +991,7 @@
       const size_t header_length_after =
           headers_->GetReadableBytesFromHeaderStream();
       if (header_length_after >= max_header_length_) {
-        HandleError(BalsaFrameEnums::HEADERS_TOO_LONG);
+        HandleHeadersTooLongError();
       }
     }
     return current - input;
@@ -1339,6 +1339,29 @@
   }
 }
 
+void BalsaFrame::HandleHeadersTooLongError() {
+  if (parse_truncated_headers_even_when_headers_too_long_) {
+    const size_t len = headers_->GetReadableBytesFromHeaderStream();
+    const char* stream_begin = headers_->OriginalHeaderStreamBegin();
+
+    if (last_slash_n_idx_ < len && stream_begin[last_slash_n_idx_] != '\r') {
+      // We write an end to the truncated line, and a blank line to end the
+      // headers, to end up with something that will parse.
+      static const absl::string_view kTwoLineEnds = "\r\n\r\n";
+      headers_->WriteFromFramer(kTwoLineEnds.data(), kTwoLineEnds.size());
+
+      // This is the last, truncated line.
+      lines_.push_back(std::make_pair(last_slash_n_idx_, len + 2));
+      // A blank line to end the headers.
+      lines_.push_back(std::make_pair(len + 2, len + 4));
+    }
+
+    ProcessHeaderLines(lines_, /*is_trailer=*/false, headers_);
+  }
+
+  HandleError(BalsaFrameEnums::HEADERS_TOO_LONG);
+}
+
 const int32_t BalsaFrame::kValidTerm1;
 const int32_t BalsaFrame::kValidTerm1Mask;
 const int32_t BalsaFrame::kValidTerm2;
diff --git a/quiche/balsa/balsa_frame.h b/quiche/balsa/balsa_frame.h
index 74e61d3..4711112 100644
--- a/quiche/balsa/balsa_frame.h
+++ b/quiche/balsa/balsa_frame.h
@@ -201,6 +201,13 @@
     use_interim_headers_callback_ = set;
   }
 
+  // If enabled, parse the available portion of headers even on a
+  // HEADERS_TOO_LONG error, so that that portion of headers is available to the
+  // error handler. Generally results in the last header being truncated.
+  void set_parse_truncated_headers_even_when_headers_too_long(bool set) {
+    parse_truncated_headers_even_when_headers_too_long_ = set;
+  }
+
  protected:
   inline BalsaHeadersEnums::ContentLengthStatus ProcessContentLengthLine(
       size_t line_idx, size_t* length);
@@ -271,6 +278,8 @@
   void HandleError(BalsaFrameEnums::ErrorCode error_code);
   void HandleWarning(BalsaFrameEnums::ErrorCode error_code);
 
+  void HandleHeadersTooLongError();
+
   bool last_char_was_slash_r_;
   bool saw_non_newline_char_;
   bool start_was_space_;
@@ -310,6 +319,9 @@
   // TODO(b/68801833): Default-enable and then deprecate this field, along with
   // set_continue_headers().
   bool use_interim_headers_callback_;
+
+  // This is not reset in Reset().
+  bool parse_truncated_headers_even_when_headers_too_long_ = false;
 };
 
 }  // namespace quiche
diff --git a/quiche/quic/core/qpack/qpack_decoded_headers_accumulator.cc b/quiche/quic/core/qpack/qpack_decoded_headers_accumulator.cc
index 41d64e7..f2588fd 100644
--- a/quiche/quic/core/qpack/qpack_decoded_headers_accumulator.cc
+++ b/quiche/quic/core/qpack/qpack_decoded_headers_accumulator.cc
@@ -46,10 +46,8 @@
           : uncompressed_header_bytes_without_overhead_;
   if (uncompressed_header_bytes > max_header_list_size_) {
     header_list_size_limit_exceeded_ = true;
-    quic_header_list_.Clear();
-  } else {
-    quic_header_list_.OnHeader(name, value);
   }
+  quic_header_list_.OnHeader(name, value);
 }
 
 void QpackDecodedHeadersAccumulator::OnDecodingCompleted() {