No public description

PiperOrigin-RevId: 944543945
diff --git a/quiche/balsa/balsa_frame.cc b/quiche/balsa/balsa_frame.cc
index 1274da1..7da637e 100644
--- a/quiche/balsa/balsa_frame.cc
+++ b/quiche/balsa/balsa_frame.cc
@@ -166,16 +166,17 @@
                         BalsaFrameEnums::ErrorCode* error_code,
                         FirstLineValidationOption whitespace_option,
                         FirstLineValidationOption multiple_spaces_option,
-                        bool& has_multiple_spaces) {
+                        bool& has_multiple_spaces, bool& has_cr_tab) {
   while (begin < end && (end[-1] == '\n' || end[-1] == '\r')) {
     --end;
   }
 
-  if (whitespace_option != FirstLineValidationOption::NONE) {
-    constexpr absl::string_view kBadWhitespace = "\r\t";
-    char* pos = std::find_first_of(begin, end, kBadWhitespace.begin(),
-                                   kBadWhitespace.end());
-    if (pos != end) {
+  constexpr absl::string_view kBadWhitespace = "\r\t";
+  char* pos = std::find_first_of(begin, end, kBadWhitespace.begin(),
+                                 kBadWhitespace.end());
+  if (pos != end) {
+    has_cr_tab = true;
+    if (whitespace_option != FirstLineValidationOption::NONE) {
       if (whitespace_option == FirstLineValidationOption::REJECT) {
         QUICHE_CODE_COUNT(sanitize_cr_tab_in_first_line_rejected);
         *error_code = static_cast<BalsaFrameEnums::ErrorCode>(
@@ -382,11 +383,13 @@
 // at most one newline, which must be at the end of the line.
 void BalsaFrame::ProcessFirstLine(char* begin, char* end) {
   bool has_multiple_spaces = false;
+  bool has_cr_tab = false;
   BalsaFrameEnums::ErrorCode previous_error = last_error_;
-  const bool parse_success = ParseHTTPFirstLine(
-      begin, end, is_request_, headers_, &last_error_,
-      http_validation_policy().sanitize_cr_tab_in_first_line,
-      http_validation_policy().sanitize_firstline_spaces, has_multiple_spaces);
+  const bool parse_success =
+      ParseHTTPFirstLine(begin, end, is_request_, headers_, &last_error_,
+                         http_validation_policy().sanitize_cr_tab_in_first_line,
+                         http_validation_policy().sanitize_firstline_spaces,
+                         has_multiple_spaces, has_cr_tab);
 
   if (!parse_success) {
     parse_state_ = BalsaFrameEnums::ERROR;
@@ -619,13 +622,14 @@
         return false;
       }
 
-      if (http_validation_policy().disallow_obs_text_in_field_names &&
-          IsObsTextChar(c)) {
-        QUICHE_CODE_COUNT(disallow_obs_text_in_field_names_enforced);
-        HandleError(is_trailer
-                        ? BalsaFrameEnums::INVALID_TRAILER_NAME_CHARACTER
-                        : BalsaFrameEnums::INVALID_HEADER_NAME_CHARACTER);
-        return false;
+      if (IsObsTextChar(c)) {
+        if (http_validation_policy().disallow_obs_text_in_field_names) {
+          QUICHE_CODE_COUNT(disallow_obs_text_in_field_names_enforced);
+          HandleError(is_trailer
+                          ? BalsaFrameEnums::INVALID_TRAILER_NAME_CHARACTER
+                          : BalsaFrameEnums::INVALID_HEADER_NAME_CHARACTER);
+          return false;
+        }
       }
     }
 
diff --git a/quiche/balsa/balsa_frame_test.cc b/quiche/balsa/balsa_frame_test.cc
index 1dedddb..6ca8ef9 100644
--- a/quiche/balsa/balsa_frame_test.cc
+++ b/quiche/balsa/balsa_frame_test.cc
@@ -821,58 +821,64 @@
     const absl::string_view parsed;    // Expected output.
     FirstLineValidationOption option;  // Whether to sanitize/reject.
     BalsaFrameEnums::ErrorCode expected_error;
+    bool expected_tab_or_cr_defect;
   };
   const std::vector<TestCase> cases = {
       // No invalid whitespace.
       {"GET / HTTP/1.1\r\n", "GET / HTTP/1.1", FirstLineValidationOption::NONE,
-       BalsaFrameEnums::BALSA_NO_ERROR},
+       BalsaFrameEnums::BALSA_NO_ERROR, false},
       {"GET / HTTP/1.1\r\n", "GET / HTTP/1.1",
-       FirstLineValidationOption::SANITIZE, BalsaFrameEnums::BALSA_NO_ERROR},
+       FirstLineValidationOption::SANITIZE, BalsaFrameEnums::BALSA_NO_ERROR,
+       false},
       {"GET / HTTP/1.1\r\n", "GET / HTTP/1.1",
-       FirstLineValidationOption::REJECT, BalsaFrameEnums::BALSA_NO_ERROR},
+       FirstLineValidationOption::REJECT, BalsaFrameEnums::BALSA_NO_ERROR,
+       false},
 
       // Illegal CR in the request-line.
       {"GET /\rHTTP/1.1\r\n", "GET /\rHTTP/1.1",
-       FirstLineValidationOption::NONE, BalsaFrameEnums::BALSA_NO_ERROR},
+       FirstLineValidationOption::NONE, BalsaFrameEnums::BALSA_NO_ERROR, true},
       {"GET /\rHTTP/1.1\r\n", "GET / HTTP/1.1",
-       FirstLineValidationOption::SANITIZE, BalsaFrameEnums::BALSA_NO_ERROR},
+       FirstLineValidationOption::SANITIZE, BalsaFrameEnums::BALSA_NO_ERROR,
+       true},
       {"GET /\rHTTP/1.1\r\n", "", FirstLineValidationOption::REJECT,
-       BalsaFrameEnums::INVALID_WS_IN_REQUEST_LINE},
+       BalsaFrameEnums::INVALID_WS_IN_REQUEST_LINE, true},
 
       // Invalid tab in the request-line.
       {"GET \t/ HTTP/1.1\r\n", "GET \t/ HTTP/1.1",
-       FirstLineValidationOption::NONE, BalsaFrameEnums::BALSA_NO_ERROR},
+       FirstLineValidationOption::NONE, BalsaFrameEnums::BALSA_NO_ERROR, true},
       {"GET \t/ HTTP/1.1\r\n", "GET  / HTTP/1.1",
-       FirstLineValidationOption::SANITIZE, BalsaFrameEnums::BALSA_NO_ERROR},
+       FirstLineValidationOption::SANITIZE, BalsaFrameEnums::BALSA_NO_ERROR,
+       true},
       {"GET \t/ HTTP/1.1\r\n", "", FirstLineValidationOption::REJECT,
-       BalsaFrameEnums::INVALID_WS_IN_REQUEST_LINE},
+       BalsaFrameEnums::INVALID_WS_IN_REQUEST_LINE, true},
 
       // Both CR and tab in the request-line.
       {"GET \t/\rHTTP/1.1 \r\n", "GET \t/\rHTTP/1.1",
-       FirstLineValidationOption::NONE, BalsaFrameEnums::BALSA_NO_ERROR},
+       FirstLineValidationOption::NONE, BalsaFrameEnums::BALSA_NO_ERROR, true},
       {"GET \t/\rHTTP/1.1 \r\n", "GET  / HTTP/1.1",
-       FirstLineValidationOption::SANITIZE, BalsaFrameEnums::BALSA_NO_ERROR},
+       FirstLineValidationOption::SANITIZE, BalsaFrameEnums::BALSA_NO_ERROR,
+       true},
       {"GET \t/\rHTTP/1.1 \r\n", "", FirstLineValidationOption::REJECT,
-       BalsaFrameEnums::INVALID_WS_IN_REQUEST_LINE},
+       BalsaFrameEnums::INVALID_WS_IN_REQUEST_LINE, true},
   };
   const absl::string_view kHeaderLineAndEnding = "Foo: bar\r\n\r\n";
-  for (auto& [firstline, parsed, ws_option, expected_error] : cases) {
+  for (const auto& tc : cases) {
     SCOPED_TRACE(
-        absl::StrCat("Input: ", absl::CEscape(firstline),
-                     " Expected output: ", absl::CEscape(parsed),
-                     " whitespace option: ", static_cast<int>(ws_option)));
-    const std::string input = absl::StrCat(firstline, kHeaderLineAndEnding);
+        absl::StrCat("Input: ", absl::CEscape(tc.input),
+                     " Expected output: ", absl::CEscape(tc.parsed),
+                     " whitespace option: ", static_cast<int>(tc.option)));
+    const std::string input = absl::StrCat(tc.input, kHeaderLineAndEnding);
 
     BalsaHeaders headers;
     BalsaFrame framer;
     HttpValidationPolicy policy;
-    policy.sanitize_cr_tab_in_first_line = ws_option;
+    policy.sanitize_cr_tab_in_first_line = tc.option;
     framer.set_http_validation_policy(policy);
     framer.set_is_request(true);
     framer.set_balsa_headers(&headers);
     framer.ProcessInput(input.data(), input.size());
-    EXPECT_EQ(headers.first_line(), parsed);
-    EXPECT_EQ(framer.ErrorCode(), expected_error);
+    EXPECT_EQ(headers.first_line(), tc.parsed);
+    EXPECT_EQ(framer.ErrorCode(), tc.expected_error);
   }
 }
 
diff --git a/quiche/balsa/balsa_headers.h b/quiche/balsa/balsa_headers.h
index da57922..9f63838 100644
--- a/quiche/balsa/balsa_headers.h
+++ b/quiche/balsa/balsa_headers.h
@@ -1066,7 +1066,7 @@
       BalsaFrameEnums::ErrorCode* error_code,
       HttpValidationPolicy::FirstLineValidationOption whitespace_option,
       HttpValidationPolicy::FirstLineValidationOption multiple_spaces_option,
-      bool& has_multiple_spaces);
+      bool& has_multiple_spaces, bool& has_cr_tab);
 
   // Reverse iterators have been removed for lack of use, refer to
   // cl/30618773 in case they are needed.