Makes a safety mechanism related to trailers configurable, and disabled by default.

Originally, trailers are queued until a data source explicitly indicates the end of data. With this change, invoking `SubmitTrailers()` is taken as a signal that all data has been sent for the stream.

PiperOrigin-RevId: 411608319
diff --git a/http2/adapter/nghttp2_adapter_test.cc b/http2/adapter/nghttp2_adapter_test.cc
index c49f1f1..d406994 100644
--- a/http2/adapter/nghttp2_adapter_test.cc
+++ b/http2/adapter/nghttp2_adapter_test.cc
@@ -1915,10 +1915,7 @@
 
   EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
   EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
-  EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "POST"));
-  EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
-  EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
-  EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one"));
+  EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(4);
   EXPECT_CALL(visitor, OnEndHeadersForStream(1));
   EXPECT_CALL(visitor, OnFrameHeader(1, 4, WINDOW_UPDATE, 0));
   EXPECT_CALL(visitor, OnWindowUpdate(1, 2000));
@@ -1937,10 +1934,7 @@
   EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
 
   int send_result = adapter->Send();
-  // Some bytes should have been serialized.
   EXPECT_EQ(0, send_result);
-  // SETTINGS ack
-  EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
   visitor.Clear();
 
   const absl::string_view kBody = "This is an example response body.";
@@ -1963,9 +1957,6 @@
   send_result = adapter->Send();
   // Some bytes should have been serialized.
   EXPECT_EQ(0, send_result);
-  // SETTINGS ack
-  EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS,
-                                            spdy::SpdyFrameType::DATA}));
   visitor.Clear();
   EXPECT_FALSE(adapter->want_write());
 
diff --git a/http2/adapter/nghttp2_util.cc b/http2/adapter/nghttp2_util.cc
index 3dc87ab..f3923a7 100644
--- a/http2/adapter/nghttp2_util.cc
+++ b/http2/adapter/nghttp2_util.cc
@@ -182,10 +182,10 @@
       QUICHE_LOG(ERROR) << "Source did not use the zero-copy API!";
       return {kError, false};
     } else {
-      if (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM) {
-        send_fin_ = false;
-      }
       const bool eof = data_flags & NGHTTP2_DATA_FLAG_EOF;
+      if (eof && (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM) == 0) {
+        send_fin_ = true;
+      }
       return {result, eof};
     }
   }
@@ -211,7 +211,7 @@
   nghttp2_data_provider provider_;
   nghttp2_send_data_callback send_data_;
   void* user_data_;
-  bool send_fin_ = true;
+  bool send_fin_ = false;
 };
 
 std::unique_ptr<DataFrameSource> MakeZeroCopyDataFrameSource(
diff --git a/http2/adapter/oghttp2_adapter_test.cc b/http2/adapter/oghttp2_adapter_test.cc
index de7d61f..7d0ca8a 100644
--- a/http2/adapter/oghttp2_adapter_test.cc
+++ b/http2/adapter/oghttp2_adapter_test.cc
@@ -1975,103 +1975,109 @@
 // trailers are queued.
 TEST(OgHttp2AdapterServerTest, ServerSubmitsTrailersWhileDataDeferred) {
   DataSavingVisitor visitor;
-  OgHttp2Adapter::Options options{.perspective = Perspective::kServer};
-  auto adapter = OgHttp2Adapter::Create(visitor, options);
+  for (const bool queue_trailers : {true, false}) {
+    OgHttp2Adapter::Options options{
+        .perspective = Perspective::kServer,
+        .trailers_require_end_data = queue_trailers};
+    auto adapter = OgHttp2Adapter::Create(visitor, options);
 
-  const std::string frames = TestFrameSequence()
-                                 .ClientPreface()
-                                 .Headers(1,
-                                          {{":method", "POST"},
-                                           {":scheme", "https"},
-                                           {":authority", "example.com"},
-                                           {":path", "/this/is/request/one"}},
-                                          /*fin=*/false)
-                                 .WindowUpdate(1, 2000)
-                                 .Data(1, "This is the request body.")
-                                 .WindowUpdate(0, 2000)
-                                 .Serialize();
-  testing::InSequence s;
+    const std::string frames = TestFrameSequence()
+                                   .ClientPreface()
+                                   .Headers(1,
+                                            {{":method", "POST"},
+                                             {":scheme", "https"},
+                                             {":authority", "example.com"},
+                                             {":path", "/this/is/request/one"}},
+                                            /*fin=*/false)
+                                   .WindowUpdate(1, 2000)
+                                   .Data(1, "This is the request body.")
+                                   .WindowUpdate(0, 2000)
+                                   .Serialize();
+    testing::InSequence s;
 
-  // Client preface (empty SETTINGS)
-  EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
-  EXPECT_CALL(visitor, OnSettingsStart());
-  EXPECT_CALL(visitor, OnSettingsEnd());
+    // Client preface (empty SETTINGS)
+    EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+    EXPECT_CALL(visitor, OnSettingsStart());
+    EXPECT_CALL(visitor, OnSettingsEnd());
 
-  EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
-  EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
-  EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "POST"));
-  EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
-  EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
-  EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one"));
-  EXPECT_CALL(visitor, OnEndHeadersForStream(1));
-  EXPECT_CALL(visitor, OnFrameHeader(1, 4, WINDOW_UPDATE, 0));
-  EXPECT_CALL(visitor, OnWindowUpdate(1, 2000));
-  EXPECT_CALL(visitor, OnFrameHeader(1, _, DATA, 0));
-  EXPECT_CALL(visitor, OnBeginDataForStream(1, _));
-  EXPECT_CALL(visitor, OnDataForStream(1, "This is the request body."));
-  EXPECT_CALL(visitor, OnFrameHeader(0, 4, WINDOW_UPDATE, 0));
-  EXPECT_CALL(visitor, OnWindowUpdate(0, 2000));
+    EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+    EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+    EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(4);
+    EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+    EXPECT_CALL(visitor, OnFrameHeader(1, 4, WINDOW_UPDATE, 0));
+    EXPECT_CALL(visitor, OnWindowUpdate(1, 2000));
+    EXPECT_CALL(visitor, OnFrameHeader(1, _, DATA, 0));
+    EXPECT_CALL(visitor, OnBeginDataForStream(1, _));
+    EXPECT_CALL(visitor, OnDataForStream(1, "This is the request body."));
+    EXPECT_CALL(visitor, OnFrameHeader(0, 4, WINDOW_UPDATE, 0));
+    EXPECT_CALL(visitor, OnWindowUpdate(0, 2000));
 
-  const int64_t result = adapter->ProcessBytes(frames);
-  EXPECT_EQ(frames.size(), static_cast<size_t>(result));
+    const int64_t result = adapter->ProcessBytes(frames);
+    EXPECT_EQ(frames.size(), static_cast<size_t>(result));
 
-  EXPECT_TRUE(adapter->want_write());
+    EXPECT_TRUE(adapter->want_write());
 
-  EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
-  EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
-  EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
-  EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+    EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+    EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+    EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+    EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
 
-  int send_result = adapter->Send();
-  // Some bytes should have been serialized.
-  EXPECT_EQ(0, send_result);
-  // SETTINGS ack
-  EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS,
-                                            spdy::SpdyFrameType::SETTINGS}));
-  visitor.Clear();
+    int send_result = adapter->Send();
+    EXPECT_EQ(0, send_result);
+    visitor.Clear();
 
-  const absl::string_view kBody = "This is an example response body.";
+    const absl::string_view kBody = "This is an example response body.";
 
-  // The body source must indicate that the end of the body is not the end of
-  // the stream.
-  auto body1 = absl::make_unique<TestDataFrameSource>(visitor, false);
-  body1->AppendPayload(kBody);
-  auto* body1_ptr = body1.get();
-  int submit_result = adapter->SubmitResponse(
-      1, ToHeaders({{":status", "200"}, {"x-comment", "Sure, sounds good."}}),
-      std::move(body1));
-  EXPECT_EQ(submit_result, 0);
-  EXPECT_TRUE(adapter->want_write());
+    // The body source must indicate that the end of the body is not the end of
+    // the stream.
+    auto body1 = absl::make_unique<TestDataFrameSource>(visitor, false);
+    body1->AppendPayload(kBody);
+    auto* body1_ptr = body1.get();
+    int submit_result = adapter->SubmitResponse(
+        1, ToHeaders({{":status", "200"}, {"x-comment", "Sure, sounds good."}}),
+        std::move(body1));
+    EXPECT_EQ(submit_result, 0);
+    EXPECT_TRUE(adapter->want_write());
 
-  EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x4));
-  EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x4, 0));
-  EXPECT_CALL(visitor, OnFrameSent(DATA, 1, _, 0x0, 0));
+    EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x4));
+    EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x4, 0));
+    EXPECT_CALL(visitor, OnFrameSent(DATA, 1, _, 0x0, 0));
 
-  send_result = adapter->Send();
-  // Some bytes should have been serialized.
-  EXPECT_EQ(0, send_result);
-  // SETTINGS ack
-  EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS,
-                                            spdy::SpdyFrameType::DATA}));
-  visitor.Clear();
-  EXPECT_FALSE(adapter->want_write());
+    send_result = adapter->Send();
+    EXPECT_EQ(0, send_result);
+    visitor.Clear();
+    EXPECT_FALSE(adapter->want_write());
 
-  int trailer_result =
-      adapter->SubmitTrailer(1, ToHeaders({{"final-status", "a-ok"}}));
-  ASSERT_EQ(trailer_result, 0);
-  // Even though there are new trailers to write, the data source has not
-  // finished writing data and is blocked.
-  EXPECT_FALSE(adapter->want_write());
+    int trailer_result =
+        adapter->SubmitTrailer(1, ToHeaders({{"final-status", "a-ok"}}));
+    ASSERT_EQ(trailer_result, 0);
+    if (queue_trailers) {
+      // Even though there are new trailers to write, the data source has not
+      // finished writing data and is blocked.
+      EXPECT_FALSE(adapter->want_write());
 
-  body1_ptr->EndData();
-  adapter->ResumeStream(1);
-  EXPECT_TRUE(adapter->want_write());
+      body1_ptr->EndData();
+      adapter->ResumeStream(1);
+      EXPECT_TRUE(adapter->want_write());
 
-  EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x5));
-  EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x5, 0));
+      EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x5));
+      EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x5, 0));
 
-  send_result = adapter->Send();
-  EXPECT_EQ(0, send_result);
+      send_result = adapter->Send();
+      EXPECT_EQ(0, send_result);
+    } else {
+      // Even though the data source has not finished sending data, the library
+      // will write the trailers anyway.
+      EXPECT_TRUE(adapter->want_write());
+
+      EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x5));
+      EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x5, 0));
+
+      send_result = adapter->Send();
+      EXPECT_EQ(0, send_result);
+      EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS}));
+    }
+  }
 }
 
 TEST(OgHttp2AdapterServerTest, ServerErrorWhileHandlingHeaders) {
diff --git a/http2/adapter/oghttp2_session.cc b/http2/adapter/oghttp2_session.cc
index 399c90c..168a9a0 100644
--- a/http2/adapter/oghttp2_session.cc
+++ b/http2/adapter/oghttp2_session.cc
@@ -451,6 +451,7 @@
     const Http2StreamId stream_id = write_scheduler_.PopNextReadyStream();
     // TODO(birenroy): Add a return value to indicate write blockage, so streams
     // aren't woken unnecessarily.
+    QUICHE_VLOG(1) << "Waking stream " << stream_id << " for writes.";
     continue_writing = WriteForStream(stream_id);
   }
   if (continue_writing == SendResult::SEND_OK) {
@@ -570,7 +571,14 @@
     bool end_data;
     std::tie(length, end_data) =
         state.outbound_body->SelectPayloadLength(available_window);
-    if (length == 0 && !end_data) {
+    QUICHE_VLOG(2) << "WriteForStream | length: " << length
+                   << " end_data: " << end_data
+                   << " trailers: " << state.trailers.get();
+    if (length == 0 && !end_data &&
+        (options_.trailers_require_end_data || state.trailers == nullptr)) {
+      // An unproductive call to SelectPayloadLength() results in this stream
+      // entering the "deferred" state only if either no trailers are available
+      // to send, or trailers require an explicit end_data before being sent.
       state.data_deferred = true;
       break;
     } else if (length == DataFrameSource::kError) {
@@ -608,7 +616,11 @@
         break;
       }
     }
-    if (end_data) {
+    if (end_data || (length == 0 && state.trailers != nullptr &&
+                     !options_.trailers_require_end_data)) {
+      // If SelectPayloadLength() returned {0, false}, and there are trailers to
+      // send, and the safety feature is disabled, it's okay to send the
+      // trailers.
       if (state.trailers != nullptr) {
         auto block_ptr = std::move(state.trailers);
         if (fin) {
@@ -727,6 +739,9 @@
     // Save trailers so they can be written once data is done.
     state.trailers =
         absl::make_unique<spdy::SpdyHeaderBlock>(ToHeaderBlock(trailers));
+    if (!options_.trailers_require_end_data) {
+      iter->second.data_deferred = false;
+    }
     if (!iter->second.data_deferred) {
       write_scheduler_.MarkStreamReady(stream_id, false);
     }
diff --git a/http2/adapter/oghttp2_session.h b/http2/adapter/oghttp2_session.h
index ad007d4..00d5f3e 100644
--- a/http2/adapter/oghttp2_session.h
+++ b/http2/adapter/oghttp2_session.h
@@ -39,6 +39,10 @@
     // Whether (as server) to send a RST_STREAM NO_ERROR when sending a fin on
     // an incomplete stream.
     bool rst_stream_no_error_when_incomplete = false;
+    // Whether (as server) to queue trailers until after a stream's data source
+    // has indicated the end of data. If false, the server will assume that
+    // submitting trailers indicates the end of data.
+    bool trailers_require_end_data = false;
   };
 
   OgHttp2Session(Http2VisitorInterface& visitor, Options options);