Fixes a bug in oghttp2 handling of responses to HEAD requests.

PiperOrigin-RevId: 488776299
diff --git a/quiche/http2/adapter/oghttp2_adapter_test.cc b/quiche/http2/adapter/oghttp2_adapter_test.cc
index d68712f..02bd8b3 100644
--- a/quiche/http2/adapter/oghttp2_adapter_test.cc
+++ b/quiche/http2/adapter/oghttp2_adapter_test.cc
@@ -3777,21 +3777,15 @@
   EXPECT_CALL(visitor, OnBeginHeadersForStream(stream_id));
   EXPECT_CALL(visitor, OnHeaderForStream).Times(2);
   EXPECT_CALL(visitor, OnEndHeadersForStream(stream_id));
-
-  // BUG: visitor does not receive the END_STREAM event.
-  // EXPECT_CALL(visitor, OnEndStream(stream_id));
+  EXPECT_CALL(visitor, OnEndStream(stream_id));
+  EXPECT_CALL(visitor,
+              OnCloseStream(stream_id, Http2ErrorCode::HTTP2_NO_ERROR));
 
   adapter->ProcessBytes(initial_frames);
 
   EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
   EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
 
-  // BUG: adapter generates a RST_STREAM PROTOCOL_ERROR.
-  EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, stream_id, 4, 0x0));
-  EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, stream_id, 4, 0x0, 1));
-  EXPECT_CALL(visitor,
-              OnCloseStream(stream_id, Http2ErrorCode::HTTP2_NO_ERROR));
-
   adapter->Send();
 }
 
diff --git a/quiche/http2/adapter/oghttp2_session.cc b/quiche/http2/adapter/oghttp2_session.cc
index c0c2c61..a760bbc 100644
--- a/quiche/http2/adapter/oghttp2_session.cc
+++ b/quiche/http2/adapter/oghttp2_session.cc
@@ -32,6 +32,8 @@
 // Corresponds to NGHTTP2_ERR_CALLBACK_FAILURE.
 const int kSendError = -902;
 
+constexpr absl::string_view kHeadValue = "HEAD";
+
 // TODO(birenroy): Consider incorporating spdy::FlagsSerializionVisitor here.
 class FrameAttributeCollector : public spdy::SpdyFrameVisitor {
  public:
@@ -1180,7 +1182,8 @@
     it->second.received_header_type = headers_handler_.header_type();
 
     // Track the content-length if the headers indicate that a body can follow.
-    it->second.can_receive_body = headers_handler_.CanReceiveBody();
+    it->second.can_receive_body =
+        headers_handler_.CanReceiveBody() && !it->second.sent_head_method;
     if (it->second.can_receive_body) {
       it->second.remaining_content_length = headers_handler_.content_length();
     }
@@ -1792,6 +1795,11 @@
     write_scheduler_.MarkStreamReady(stream_id, false);
   }
   iter->second.user_data = user_data;
+  for (const auto& [name, value] : headers) {
+    if (name == kHttp2MethodPseudoHeader && value == kHeadValue) {
+      iter->second.sent_head_method = true;
+    }
+  }
   SendHeaders(stream_id, std::move(headers), end_stream);
 }
 
diff --git a/quiche/http2/adapter/oghttp2_session.h b/quiche/http2/adapter/oghttp2_session.h
index 342f773..9b31fa4 100644
--- a/quiche/http2/adapter/oghttp2_session.h
+++ b/quiche/http2/adapter/oghttp2_session.h
@@ -233,6 +233,7 @@
     bool half_closed_remote = false;
     // Indicates that `outbound_body` temporarily cannot produce data.
     bool data_deferred = false;
+    bool sent_head_method = false;
     bool can_receive_body = true;
   };
   using StreamStateMap = absl::flat_hash_map<Http2StreamId, StreamState>;