Add expect_final_byte_offset argument to CopyAndValidateTrailers().

This will be used in cr/231494986 where trailers sent on the
request/response stream should not have the :final-offset pseudo-header.

gfe-relnote: n/a.  No functional change.
PiperOrigin-RevId: 243730777
Change-Id: I1a5c729fb5aab3e3abdb0ce64dad5667faf794bf
diff --git a/epoll_server/platform/api/epoll_address_test_utils.h b/epoll_server/platform/api/epoll_address_test_utils.h
index ae87ded..e224afb 100644
--- a/epoll_server/platform/api/epoll_address_test_utils.h
+++ b/epoll_server/platform/api/epoll_address_test_utils.h
@@ -5,7 +5,7 @@
 #ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_ADDRESS_TEST_UTILS_H_
 #define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_ADDRESS_TEST_UTILS_H_
 
-#include "net/epoll_server/platform/impl/epoll_address_test_utils_impl.h"
+#include "net/tools/epoll_server/platform/impl/epoll_address_test_utils_impl.h"
 
 namespace epoll_server {
 
diff --git a/epoll_server/platform/api/epoll_bug.h b/epoll_server/platform/api/epoll_bug.h
index d90c427..33a3478 100644
--- a/epoll_server/platform/api/epoll_bug.h
+++ b/epoll_server/platform/api/epoll_bug.h
@@ -5,7 +5,7 @@
 #ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_BUG_H_
 #define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_BUG_H_
 
-#include "net/epoll_server/platform/impl/epoll_bug_impl.h"
+#include "net/tools/epoll_server/platform/impl/epoll_bug_impl.h"
 
 #define EPOLL_BUG EPOLL_BUG_IMPL
 
diff --git a/epoll_server/platform/api/epoll_expect_bug.h b/epoll_server/platform/api/epoll_expect_bug.h
index a7795c6..313823c 100644
--- a/epoll_server/platform/api/epoll_expect_bug.h
+++ b/epoll_server/platform/api/epoll_expect_bug.h
@@ -5,7 +5,7 @@
 #ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_EXPECT_BUG_H_
 #define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_EXPECT_BUG_H_
 
-#include "net/epoll_server/platform/impl/epoll_expect_bug_impl.h"
+#include "net/tools/epoll_server/platform/impl/epoll_expect_bug_impl.h"
 
 #define EXPECT_EPOLL_BUG EXPECT_EPOLL_BUG_IMPL
 
diff --git a/epoll_server/platform/api/epoll_logging.h b/epoll_server/platform/api/epoll_logging.h
index bcd9b96..3cd2fac 100644
--- a/epoll_server/platform/api/epoll_logging.h
+++ b/epoll_server/platform/api/epoll_logging.h
@@ -5,7 +5,7 @@
 #ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_LOGGING_H_
 #define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_LOGGING_H_
 
-#include "net/epoll_server/platform/impl/epoll_logging_impl.h"
+#include "net/tools/epoll_server/platform/impl/epoll_logging_impl.h"
 
 namespace epoll_server {
 
diff --git a/epoll_server/platform/api/epoll_test.h b/epoll_server/platform/api/epoll_test.h
index e5a0524..89214b4 100644
--- a/epoll_server/platform/api/epoll_test.h
+++ b/epoll_server/platform/api/epoll_test.h
@@ -5,7 +5,7 @@
 #ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TEST_H_
 #define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TEST_H_
 
-#include "net/epoll_server/platform/impl/epoll_test_impl.h"
+#include "net/tools/epoll_server/platform/impl/epoll_test_impl.h"
 #define EpollTest EpollTestImpl
 
 #endif  // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TEST_H_
diff --git a/epoll_server/platform/api/epoll_thread.h b/epoll_server/platform/api/epoll_thread.h
index a41dd28..97c0b08 100644
--- a/epoll_server/platform/api/epoll_thread.h
+++ b/epoll_server/platform/api/epoll_thread.h
@@ -7,7 +7,7 @@
 
 #include <string>
 
-#include "net/epoll_server/platform/impl/epoll_thread_impl.h"
+#include "net/tools/epoll_server/platform/impl/epoll_thread_impl.h"
 
 namespace epoll_server {
 
diff --git a/epoll_server/platform/api/epoll_time.h b/epoll_server/platform/api/epoll_time.h
index 9fefaa7..95f0407 100644
--- a/epoll_server/platform/api/epoll_time.h
+++ b/epoll_server/platform/api/epoll_time.h
@@ -5,7 +5,7 @@
 #ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TIME_H_
 #define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TIME_H_
 
-#include "net/epoll_server/platform/impl/epoll_time_impl.h"
+#include "net/tools/epoll_server/platform/impl/epoll_time_impl.h"
 
 namespace epoll_server {
 
diff --git a/quic/core/http/quic_spdy_stream.cc b/quic/core/http/quic_spdy_stream.cc
index fd77668..674b076 100644
--- a/quic/core/http/quic_spdy_stream.cc
+++ b/quic/core/http/quic_spdy_stream.cc
@@ -449,7 +449,9 @@
   }
 
   size_t final_byte_offset = 0;
-  if (!SpdyUtils::CopyAndValidateTrailers(header_list, &final_byte_offset,
+  if (!SpdyUtils::CopyAndValidateTrailers(header_list,
+                                          /* expect_final_byte_offset = */ true,
+                                          &final_byte_offset,
                                           &received_trailers_)) {
     QUIC_DLOG(ERROR) << "Trailers for stream " << id() << " are malformed.";
     session()->connection()->CloseConnection(
diff --git a/quic/core/http/spdy_utils.cc b/quic/core/http/spdy_utils.cc
index 2d94fd4..721ae33 100644
--- a/quic/core/http/spdy_utils.cc
+++ b/quic/core/http/spdy_utils.cc
@@ -86,6 +86,7 @@
 }
 
 bool SpdyUtils::CopyAndValidateTrailers(const QuicHeaderList& header_list,
+                                        bool expect_final_byte_offset,
                                         size_t* final_byte_offset,
                                         SpdyHeaderBlock* trailers) {
   bool found_final_byte_offset = false;
@@ -94,7 +95,8 @@
 
     // Pull out the final offset pseudo header which indicates the number of
     // response body bytes expected.
-    if (!found_final_byte_offset && name == kFinalOffsetHeaderKey &&
+    if (expect_final_byte_offset && !found_final_byte_offset &&
+        name == kFinalOffsetHeaderKey &&
         QuicTextUtils::StringToSizeT(p.second, final_byte_offset)) {
       found_final_byte_offset = true;
       continue;
@@ -116,7 +118,7 @@
     trailers->AppendValueOrAddHeader(name, p.second);
   }
 
-  if (!found_final_byte_offset) {
+  if (expect_final_byte_offset && !found_final_byte_offset) {
     QUIC_DLOG(ERROR) << "Required key '" << kFinalOffsetHeaderKey
                      << "' not present";
     return false;
diff --git a/quic/core/http/spdy_utils.h b/quic/core/http/spdy_utils.h
index 444dd7f..dc3fabc 100644
--- a/quic/core/http/spdy_utils.h
+++ b/quic/core/http/spdy_utils.h
@@ -32,7 +32,17 @@
                                      spdy::SpdyHeaderBlock* headers);
 
   // Copies a list of headers to a SpdyHeaderBlock.
+  // If |expect_final_byte_offset| is true, requires exactly one header field
+  // with key kFinalOffsetHeaderKey and an integer value.
+  // If |expect_final_byte_offset| is false, no kFinalOffsetHeaderKey may be
+  // present.
+  // Returns true if parsing is successful.  Returns false if the presence of
+  // kFinalOffsetHeaderKey does not match the value of
+  // |expect_final_byte_offset|, the kFinalOffsetHeaderKey value cannot be
+  // parsed, any other pseudo-header is present, an empty header key is present,
+  // or a header key contains an uppercase character.
   static bool CopyAndValidateTrailers(const QuicHeaderList& header_list,
+                                      bool expect_final_byte_offset,
                                       size_t* final_byte_offset,
                                       spdy::SpdyHeaderBlock* trailers);
 
diff --git a/quic/core/http/spdy_utils_test.cc b/quic/core/http/spdy_utils_test.cc
index b7da0fd..d5c96fb 100644
--- a/quic/core/http/spdy_utils_test.cc
+++ b/quic/core/http/spdy_utils_test.cc
@@ -17,6 +17,10 @@
 
 namespace quic {
 namespace test {
+namespace {
+
+const bool kExpectFinalByteOffset = true;
+const bool kDoNotExpectFinalByteOffset = false;
 
 static std::unique_ptr<QuicHeaderList> FromList(
     const QuicHeaderList::ListType& src) {
@@ -29,6 +33,8 @@
   return headers;
 }
 
+}  // anonymous namespace
+
 using CopyAndValidateHeaders = QuicTest;
 
 TEST_F(CopyAndValidateHeaders, NormalUsage) {
@@ -202,29 +208,62 @@
   auto trailers = FromList({{kFinalOffsetHeaderKey, "1234"}});
   size_t final_byte_offset = 0;
   SpdyHeaderBlock block;
-  EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(*trailers, &final_byte_offset,
-                                                 &block));
+  EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(
+      *trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
   EXPECT_EQ(1234u, final_byte_offset);
 }
 
-TEST_F(CopyAndValidateTrailers, EmptyTrailerList) {
-  // An empty trailer list will fail as required key kFinalOffsetHeaderKey is
+TEST_F(CopyAndValidateTrailers, EmptyTrailerListWithFinalByteOffsetExpected) {
+  // An empty trailer list will fail as expected key kFinalOffsetHeaderKey is
   // not present.
   QuicHeaderList trailers;
   size_t final_byte_offset = 0;
   SpdyHeaderBlock block;
-  EXPECT_FALSE(
-      SpdyUtils::CopyAndValidateTrailers(trailers, &final_byte_offset, &block));
+  EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(
+      trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
 }
 
-TEST_F(CopyAndValidateTrailers, FinalByteOffsetNotPresent) {
-  // Validation fails if required kFinalOffsetHeaderKey is not present, even if
+TEST_F(CopyAndValidateTrailers,
+       EmptyTrailerListWithFinalByteOffsetNotExpected) {
+  // An empty trailer list will pass successfully if kFinalOffsetHeaderKey is
+  // not expected.
+  QuicHeaderList trailers;
+  size_t final_byte_offset = 0;
+  SpdyHeaderBlock block;
+  EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(
+      trailers, kDoNotExpectFinalByteOffset, &final_byte_offset, &block));
+  EXPECT_TRUE(block.empty());
+}
+
+TEST_F(CopyAndValidateTrailers, FinalByteOffsetExpectedButNotPresent) {
+  // Validation fails if expected kFinalOffsetHeaderKey is not present, even if
   // the rest of the header block is valid.
   auto trailers = FromList({{"key", "value"}});
   size_t final_byte_offset = 0;
   SpdyHeaderBlock block;
-  EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(*trailers, &final_byte_offset,
-                                                  &block));
+  EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(
+      *trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
+}
+
+TEST_F(CopyAndValidateTrailers, FinalByteOffsetNotExpectedButPresent) {
+  // Validation fails if kFinalOffsetHeaderKey is present but should not be,
+  // even if the rest of the header block is valid.
+  auto trailers = FromList({{"key", "value"}, {kFinalOffsetHeaderKey, "1234"}});
+  size_t final_byte_offset = 0;
+  SpdyHeaderBlock block;
+  EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(
+      *trailers, kDoNotExpectFinalByteOffset, &final_byte_offset, &block));
+}
+
+TEST_F(CopyAndValidateTrailers, FinalByteOffsetNotExpectedAndNotPresent) {
+  // Validation succeeds if kFinalOffsetHeaderKey is not expected and not
+  // present.
+  auto trailers = FromList({{"key", "value"}});
+  size_t final_byte_offset = 0;
+  SpdyHeaderBlock block;
+  EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(
+      *trailers, kDoNotExpectFinalByteOffset, &final_byte_offset, &block));
+  EXPECT_THAT(block, UnorderedElementsAre(Pair("key", "value")));
 }
 
 TEST_F(CopyAndValidateTrailers, EmptyName) {
@@ -233,8 +272,8 @@
   auto trailers = FromList({{"", "value"}, {kFinalOffsetHeaderKey, "1234"}});
   size_t final_byte_offset = 0;
   SpdyHeaderBlock block;
-  EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(*trailers, &final_byte_offset,
-                                                  &block));
+  EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(
+      *trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
 }
 
 TEST_F(CopyAndValidateTrailers, PseudoHeaderInTrailers) {
@@ -243,8 +282,8 @@
       FromList({{":pseudo_key", "value"}, {kFinalOffsetHeaderKey, "1234"}});
   size_t final_byte_offset = 0;
   SpdyHeaderBlock block;
-  EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(*trailers, &final_byte_offset,
-                                                  &block));
+  EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(
+      *trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
 }
 
 TEST_F(CopyAndValidateTrailers, DuplicateTrailers) {
@@ -262,8 +301,8 @@
                             {"key", "non_contiguous_duplicate"}});
   size_t final_byte_offset = 0;
   SpdyHeaderBlock block;
-  EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(*trailers, &final_byte_offset,
-                                                 &block));
+  EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(
+      *trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
   EXPECT_THAT(
       block,
       UnorderedElementsAre(
@@ -286,8 +325,8 @@
 
   size_t final_byte_offset = 0;
   SpdyHeaderBlock block;
-  EXPECT_TRUE(
-      SpdyUtils::CopyAndValidateTrailers(*headers, &final_byte_offset, &block));
+  EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(
+      *headers, kExpectFinalByteOffset, &final_byte_offset, &block));
   EXPECT_THAT(
       block,
       UnorderedElementsAre(