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>;