Moves metadata-related tests to a separate test file. PiperOrigin-RevId: 565185742
diff --git a/build/source_list.bzl b/build/source_list.bzl index 10603ec..b008e48 100644 --- a/build/source_list.bzl +++ b/build/source_list.bzl
@@ -1077,6 +1077,7 @@ "http2/adapter/event_forwarder_test.cc", "http2/adapter/header_validator_test.cc", "http2/adapter/noop_header_validator_test.cc", + "http2/adapter/oghttp2_adapter_metadata_test.cc", "http2/adapter/oghttp2_adapter_test.cc", "http2/adapter/oghttp2_session_test.cc", "http2/adapter/oghttp2_util_test.cc",
diff --git a/build/source_list.gni b/build/source_list.gni index 2cd0ddd..23b3066 100644 --- a/build/source_list.gni +++ b/build/source_list.gni
@@ -1078,6 +1078,7 @@ "src/quiche/http2/adapter/event_forwarder_test.cc", "src/quiche/http2/adapter/header_validator_test.cc", "src/quiche/http2/adapter/noop_header_validator_test.cc", + "src/quiche/http2/adapter/oghttp2_adapter_metadata_test.cc", "src/quiche/http2/adapter/oghttp2_adapter_test.cc", "src/quiche/http2/adapter/oghttp2_session_test.cc", "src/quiche/http2/adapter/oghttp2_util_test.cc",
diff --git a/build/source_list.json b/build/source_list.json index f41f490..ef16ce6 100644 --- a/build/source_list.json +++ b/build/source_list.json
@@ -1077,6 +1077,7 @@ "quiche/http2/adapter/event_forwarder_test.cc", "quiche/http2/adapter/header_validator_test.cc", "quiche/http2/adapter/noop_header_validator_test.cc", + "quiche/http2/adapter/oghttp2_adapter_metadata_test.cc", "quiche/http2/adapter/oghttp2_adapter_test.cc", "quiche/http2/adapter/oghttp2_session_test.cc", "quiche/http2/adapter/oghttp2_util_test.cc",
diff --git a/quiche/http2/adapter/oghttp2_adapter_metadata_test.cc b/quiche/http2/adapter/oghttp2_adapter_metadata_test.cc new file mode 100644 index 0000000..d131daf --- /dev/null +++ b/quiche/http2/adapter/oghttp2_adapter_metadata_test.cc
@@ -0,0 +1,636 @@ +#include "quiche/http2/adapter/oghttp2_adapter.h" + +#include <cstdint> +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/str_join.h" +#include "quiche/http2/adapter/http2_protocol.h" +#include "quiche/http2/adapter/http2_visitor_interface.h" +#include "quiche/http2/adapter/mock_http2_visitor.h" +#include "quiche/http2/adapter/oghttp2_util.h" +#include "quiche/http2/adapter/test_frame_sequence.h" +#include "quiche/http2/adapter/test_utils.h" +#include "quiche/common/platform/api/quiche_expect_bug.h" +#include "quiche/common/platform/api/quiche_test.h" +#include "quiche/spdy/core/http2_header_block.h" + +namespace http2 { +namespace adapter { +namespace test { +namespace { + +using ConnectionError = Http2VisitorInterface::ConnectionError; + +using spdy::SpdyFrameType; +using testing::_; + +enum FrameType { + DATA, + HEADERS, + PRIORITY, + RST_STREAM, + SETTINGS, + PUSH_PROMISE, + PING, + GOAWAY, + WINDOW_UPDATE, + CONTINUATION, +}; + +TEST(OgHttp2AdapterTest, ClientHandlesMetadata) { + DataSavingVisitor visitor; + OgHttp2Adapter::Options options; + options.perspective = Perspective::kClient; + auto adapter = OgHttp2Adapter::Create(visitor, options); + + testing::InSequence s; + + const std::vector<Header> headers1 = + ToHeaders({{":method", "GET"}, + {":scheme", "http"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}}); + + const char* kSentinel1 = "arbitrary pointer 1"; + const int32_t stream_id1 = + adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1)); + ASSERT_GT(stream_id1, 0); + QUICHE_LOG(INFO) << "Created stream: " << stream_id1; + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, + END_STREAM_FLAG | END_HEADERS_FLAG)); + EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, + END_STREAM_FLAG | END_HEADERS_FLAG, 0)); + + int result = adapter->Send(); + EXPECT_EQ(0, result); + absl::string_view data = visitor.data(); + EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); + data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); + EXPECT_THAT(data, + EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS})); + visitor.Clear(); + + const std::string stream_frames = + TestFrameSequence() + .ServerPreface() + .Metadata(0, "Example connection metadata") + .Headers(1, + {{":status", "200"}, + {"server", "my-fake-server"}, + {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, + /*fin=*/false) + .Metadata(1, "Example stream metadata") + .Data(1, "This is the response body.", true) + .Serialize(); + + // Server preface (empty SETTINGS) + EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); + EXPECT_CALL(visitor, OnSettingsStart()); + EXPECT_CALL(visitor, OnSettingsEnd()); + + EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 4)); + EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _)); + EXPECT_CALL(visitor, OnMetadataForStream(0, _)); + EXPECT_CALL(visitor, OnMetadataEndForStream(0)); + EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); + EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200")); + EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server")); + EXPECT_CALL(visitor, + OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT")); + EXPECT_CALL(visitor, OnEndHeadersForStream(1)); + EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 4)); + EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _)); + EXPECT_CALL(visitor, OnMetadataForStream(1, _)); + EXPECT_CALL(visitor, OnMetadataEndForStream(1)); + EXPECT_CALL(visitor, OnFrameHeader(1, 26, DATA, 1)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 26)); + EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body.")); + EXPECT_CALL(visitor, OnEndStream(1)); + EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); + + const int64_t stream_result = adapter->ProcessBytes(stream_frames); + EXPECT_EQ(stream_frames.size(), static_cast<size_t>(stream_result)); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, ACK_FLAG)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, ACK_FLAG, 0)); + + EXPECT_TRUE(adapter->want_write()); + result = adapter->Send(); + EXPECT_EQ(0, result); + EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS})); +} + +TEST(OgHttp2AdapterTest, ClientHandlesMetadataWithEmptyPayload) { + DataSavingVisitor visitor; + OgHttp2Adapter::Options options; + options.perspective = Perspective::kClient; + auto adapter = OgHttp2Adapter::Create(visitor, options); + + testing::InSequence s; + + const std::vector<Header> headers1 = + ToHeaders({{":method", "GET"}, + {":scheme", "http"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}}); + + const int32_t stream_id = adapter->SubmitRequest(headers1, nullptr, nullptr); + ASSERT_GT(stream_id, 0); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, + END_STREAM_FLAG | END_HEADERS_FLAG)); + EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, + END_STREAM_FLAG | END_HEADERS_FLAG, 0)); + + int result = adapter->Send(); + EXPECT_EQ(0, result); + absl::string_view data = visitor.data(); + EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); + data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); + EXPECT_THAT(data, + EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS})); + visitor.Clear(); + + const std::string stream_frames = + TestFrameSequence() + .ServerPreface() + .Headers(1, + {{":status", "200"}, + {"server", "my-fake-server"}, + {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, + /*fin=*/false) + .Metadata(1, "") + .Data(1, "This is the response body.", true) + .Serialize(); + + // Server 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, _, _)).Times(3); + EXPECT_CALL(visitor, OnEndHeadersForStream(1)); + EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 4)); + EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _)); + EXPECT_CALL(visitor, OnMetadataEndForStream(1)); + EXPECT_CALL(visitor, OnFrameHeader(1, _, DATA, 1)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, _)); + EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body.")); + EXPECT_CALL(visitor, OnEndStream(1)); + EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); + + const int64_t stream_result = adapter->ProcessBytes(stream_frames); + EXPECT_EQ(stream_frames.size(), static_cast<size_t>(stream_result)); +} + +TEST(OgHttp2AdapterTest, ClientHandlesMetadataWithPayloadError) { + DataSavingVisitor visitor; + OgHttp2Adapter::Options options; + options.perspective = Perspective::kClient; + auto adapter = OgHttp2Adapter::Create(visitor, options); + + testing::InSequence s; + + const std::vector<Header> headers = + ToHeaders({{":method", "GET"}, + {":scheme", "http"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}}); + + const int32_t stream_id = adapter->SubmitRequest(headers, nullptr, nullptr); + ASSERT_GT(stream_id, 0); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, + END_STREAM_FLAG | END_HEADERS_FLAG)); + EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, + END_STREAM_FLAG | END_HEADERS_FLAG, 0)); + + int result = adapter->Send(); + EXPECT_EQ(0, result); + visitor.Clear(); + + const std::string stream_frames = + TestFrameSequence() + .ServerPreface() + .Metadata(0, "Example connection metadata") + .Headers(stream_id, + {{":status", "200"}, + {"server", "my-fake-server"}, + {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, + /*fin=*/false) + .Metadata(stream_id, "Example stream metadata") + .Data(stream_id, "This is the response body.", true) + .Serialize(); + + // Server preface (empty SETTINGS) + EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); + EXPECT_CALL(visitor, OnSettingsStart()); + EXPECT_CALL(visitor, OnSettingsEnd()); + + EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 4)); + EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _)); + EXPECT_CALL(visitor, OnMetadataForStream(0, _)); + EXPECT_CALL(visitor, OnMetadataEndForStream(0)); + EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, HEADERS, 4)); + EXPECT_CALL(visitor, OnBeginHeadersForStream(stream_id)); + EXPECT_CALL(visitor, OnHeaderForStream(stream_id, _, _)).Times(3); + EXPECT_CALL(visitor, OnEndHeadersForStream(stream_id)); + EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, kMetadataFrameType, 4)); + EXPECT_CALL(visitor, OnBeginMetadataForStream(stream_id, _)); + EXPECT_CALL(visitor, OnMetadataForStream(stream_id, _)) + .WillOnce(testing::Return(false)); + // Remaining frames are not processed due to the error. + EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kParseError)); + + const int64_t stream_result = adapter->ProcessBytes(stream_frames); + // Negative integer returned to indicate an error. + EXPECT_LT(stream_result, 0); + + EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); + EXPECT_CALL(visitor, + OnFrameSent(GOAWAY, 0, _, 0x0, + static_cast<int>(Http2ErrorCode::INTERNAL_ERROR))); + + EXPECT_FALSE(adapter->want_read()); + EXPECT_TRUE(adapter->want_write()); + result = adapter->Send(); + EXPECT_EQ(0, result); + EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::GOAWAY})); +} + +TEST(OgHttp2AdapterTest, ClientHandlesMetadataWithCompletionError) { + DataSavingVisitor visitor; + OgHttp2Adapter::Options options; + options.perspective = Perspective::kClient; + auto adapter = OgHttp2Adapter::Create(visitor, options); + + testing::InSequence s; + + const std::vector<Header> headers = + ToHeaders({{":method", "GET"}, + {":scheme", "http"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}}); + + const int32_t stream_id = adapter->SubmitRequest(headers, nullptr, nullptr); + ASSERT_GT(stream_id, 0); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, + END_STREAM_FLAG | END_HEADERS_FLAG)); + EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, + END_STREAM_FLAG | END_HEADERS_FLAG, 0)); + + int result = adapter->Send(); + EXPECT_EQ(0, result); + visitor.Clear(); + + const std::string stream_frames = + TestFrameSequence() + .ServerPreface() + .Metadata(0, "Example connection metadata") + .Headers(stream_id, + {{":status", "200"}, + {"server", "my-fake-server"}, + {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, + /*fin=*/false) + .Metadata(stream_id, "Example stream metadata") + .Data(stream_id, "This is the response body.", true) + .Serialize(); + + // Server preface (empty SETTINGS) + EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); + EXPECT_CALL(visitor, OnSettingsStart()); + EXPECT_CALL(visitor, OnSettingsEnd()); + + EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 4)); + EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _)); + EXPECT_CALL(visitor, OnMetadataForStream(0, _)); + EXPECT_CALL(visitor, OnMetadataEndForStream(0)); + EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, HEADERS, 4)); + EXPECT_CALL(visitor, OnBeginHeadersForStream(stream_id)); + EXPECT_CALL(visitor, OnHeaderForStream(stream_id, _, _)).Times(3); + EXPECT_CALL(visitor, OnEndHeadersForStream(stream_id)); + EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, kMetadataFrameType, 4)); + EXPECT_CALL(visitor, OnBeginMetadataForStream(stream_id, _)); + EXPECT_CALL(visitor, OnMetadataForStream(stream_id, _)); + EXPECT_CALL(visitor, OnMetadataEndForStream(stream_id)) + .WillOnce(testing::Return(false)); + // Remaining frames are not processed due to the error. + EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kParseError)); + + const int64_t stream_result = adapter->ProcessBytes(stream_frames); + // Negative integer returned to indicate an error. + EXPECT_LT(stream_result, 0); + + EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); + EXPECT_CALL(visitor, + OnFrameSent(GOAWAY, 0, _, 0x0, + static_cast<int>(Http2ErrorCode::INTERNAL_ERROR))); + + EXPECT_FALSE(adapter->want_read()); + EXPECT_TRUE(adapter->want_write()); + result = adapter->Send(); + EXPECT_EQ(0, result); + EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::GOAWAY})); +} + +TEST(OgHttp2AdapterTest, ClientSendsMetadataAfterFlowControlBlock) { + DataSavingVisitor visitor; + OgHttp2Adapter::Options options; + options.perspective = Perspective::kClient; + auto adapter = OgHttp2Adapter::Create(visitor, options); + + testing::InSequence s; + + const std::vector<Header> headers1 = + ToHeaders({{":method", "GET"}, + {":scheme", "http"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}}); + + const std::string kBody = std::string(100 * 1024, 'a'); + auto body1 = std::make_unique<TestDataFrameSource>(visitor, false); + body1->AppendPayload(kBody); + body1->EndData(); + + const int32_t stream_id1 = + adapter->SubmitRequest(headers1, std::move(body1), nullptr); + ASSERT_GT(stream_id1, 0); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x4)); + EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x4, 0)); + // 4 DATA frames should saturate the default 64kB stream/connection flow + // control window. + EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id1, _, 0x0, 0)).Times(4); + + int result = adapter->Send(); + EXPECT_EQ(0, result); + EXPECT_FALSE(adapter->want_write()); + EXPECT_EQ(0, adapter->GetSendWindowSize()); + + auto source = std::make_unique<TestMetadataSource>(ToHeaderBlock(ToHeaders( + {{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}}))); + adapter->SubmitMetadata(1, 16384u, std::move(source)); + EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x4)); + EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x4, 0)); + + result = adapter->Send(); + EXPECT_EQ(0, result); +} + +TEST(OgHttp2AdapterTest, ClientSendsMetadataWithContinuation) { + DataSavingVisitor visitor; + OgHttp2Adapter::Options options; + options.perspective = Perspective::kServer; + auto adapter = OgHttp2Adapter::Create(visitor, options); + EXPECT_FALSE(adapter->want_write()); + + const std::string frames = + TestFrameSequence() + .ClientPreface() + .Metadata(0, "Example connection metadata in multiple frames", true) + .Headers(1, + {{":method", "GET"}, + {":scheme", "https"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}}, + /*fin=*/false, + /*add_continuation=*/true) + .Metadata(1, + "Some stream metadata that's also sent in multiple frames", + true) + .Serialize(); + testing::InSequence s; + + // Client preface (empty SETTINGS) + EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); + EXPECT_CALL(visitor, OnSettingsStart()); + EXPECT_CALL(visitor, OnSettingsEnd()); + // Metadata on stream 0 + EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 0)); + EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _)); + EXPECT_CALL(visitor, OnMetadataForStream(0, _)); + EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 4)); + EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _)); + EXPECT_CALL(visitor, OnMetadataForStream(0, _)); + EXPECT_CALL(visitor, OnMetadataEndForStream(0)); + + // Stream 1 + EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 0)); + EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "GET")); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https")); + EXPECT_CALL(visitor, OnFrameHeader(1, _, CONTINUATION, 4)); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com")); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one")); + EXPECT_CALL(visitor, OnEndHeadersForStream(1)); + // Metadata on stream 1 + EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 0)); + EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _)); + EXPECT_CALL(visitor, OnMetadataForStream(1, _)); + EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 4)); + EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _)); + EXPECT_CALL(visitor, OnMetadataForStream(1, _)); + EXPECT_CALL(visitor, OnMetadataEndForStream(1)); + + const int64_t result = adapter->ProcessBytes(frames); + EXPECT_EQ(frames.size(), static_cast<size_t>(result)); + EXPECT_EQ("Example connection metadata in multiple frames", + absl::StrJoin(visitor.GetMetadata(0), "")); + EXPECT_EQ("Some stream metadata that's also sent in multiple frames", + absl::StrJoin(visitor.GetMetadata(1), "")); +} + +TEST(OgHttp2AdapterTest, SubmitMetadata) { + DataSavingVisitor visitor; + OgHttp2Adapter::Options options; + options.perspective = Perspective::kServer; + auto adapter = OgHttp2Adapter::Create(visitor, options); + + auto source = std::make_unique<TestMetadataSource>(ToHeaderBlock(ToHeaders( + {{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}}))); + adapter->SubmitMetadata(1, 16384u, std::move(source)); + EXPECT_TRUE(adapter->want_write()); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x4)); + EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x4, 0)); + + int result = adapter->Send(); + EXPECT_EQ(0, result); + EXPECT_THAT(visitor.data(), + EqualsFrames({SpdyFrameType::SETTINGS, + static_cast<SpdyFrameType>(kMetadataFrameType)})); + EXPECT_FALSE(adapter->want_write()); +} + +TEST(OgHttp2AdapterTest, SubmitMetadataMultipleFrames) { + DataSavingVisitor visitor; + OgHttp2Adapter::Options options; + options.perspective = Perspective::kServer; + auto adapter = OgHttp2Adapter::Create(visitor, options); + + const auto kLargeValue = std::string(63 * 1024, 'a'); + auto source = std::make_unique<TestMetadataSource>( + ToHeaderBlock(ToHeaders({{"large-value", kLargeValue}}))); + adapter->SubmitMetadata(1, 16384u, std::move(source)); + EXPECT_TRUE(adapter->want_write()); + + testing::InSequence seq; + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x4)); + EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x4, 0)); + + int result = adapter->Send(); + EXPECT_EQ(0, result); + absl::string_view serialized = visitor.data(); + EXPECT_THAT(serialized, + EqualsFrames({SpdyFrameType::SETTINGS, + static_cast<SpdyFrameType>(kMetadataFrameType), + static_cast<SpdyFrameType>(kMetadataFrameType), + static_cast<SpdyFrameType>(kMetadataFrameType), + static_cast<SpdyFrameType>(kMetadataFrameType)})); + EXPECT_FALSE(adapter->want_write()); +} + +TEST(OgHttp2AdapterTest, SubmitConnectionMetadata) { + DataSavingVisitor visitor; + OgHttp2Adapter::Options options; + options.perspective = Perspective::kServer; + auto adapter = OgHttp2Adapter::Create(visitor, options); + + auto source = std::make_unique<TestMetadataSource>(ToHeaderBlock(ToHeaders( + {{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}}))); + adapter->SubmitMetadata(0, 16384u, std::move(source)); + EXPECT_TRUE(adapter->want_write()); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 0, _, 0x4)); + EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 0, _, 0x4, 0)); + + int result = adapter->Send(); + EXPECT_EQ(0, result); + EXPECT_THAT(visitor.data(), + EqualsFrames({SpdyFrameType::SETTINGS, + static_cast<SpdyFrameType>(kMetadataFrameType)})); + EXPECT_FALSE(adapter->want_write()); +} + +TEST(OgHttp2AdapterTest, ServerQueuesMetadataThenTrailers) { + DataSavingVisitor visitor; + OgHttp2Adapter::Options options; + options.perspective = Perspective::kServer; + auto adapter = OgHttp2Adapter::Create(visitor, options); + EXPECT_FALSE(adapter->want_write()); + + const std::string frames = TestFrameSequence() + .ClientPreface() + .Headers(1, + {{":method", "GET"}, + {":scheme", "https"}, + {":authority", "example.com"}, + {":path", "/"}}, + /*fin=*/true) + .Serialize(); + testing::InSequence s; + + // Client preface (empty SETTINGS) + EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); + EXPECT_CALL(visitor, OnSettingsStart()); + EXPECT_CALL(visitor, OnSettingsEnd()); + // Stream 1 + EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5)); + EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); + EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(4); + EXPECT_CALL(visitor, OnEndHeadersForStream(1)); + EXPECT_CALL(visitor, OnEndStream(1)); + + const int64_t result = adapter->ProcessBytes(frames); + EXPECT_EQ(frames.size(), static_cast<size_t>(result)); + + 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 = std::make_unique<TestDataFrameSource>(visitor, false); + body1->AppendPayload(kBody); + body1->EndData(); + 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(SETTINGS, 0, _, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, ACK_FLAG)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, ACK_FLAG, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x4)); + EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x4, 0)); + EXPECT_CALL(visitor, OnFrameSent(DATA, 1, _, 0x0, 0)); + + int send_result = adapter->Send(); + EXPECT_EQ(0, send_result); + EXPECT_THAT(visitor.data(), + EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, + SpdyFrameType::HEADERS, SpdyFrameType::DATA})); + EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody)); + visitor.Clear(); + EXPECT_FALSE(adapter->want_write()); + + spdy::Http2HeaderBlock block; + block["key"] = "wild value!"; + adapter->SubmitMetadata( + 1, 16384u, std::make_unique<TestMetadataSource>(std::move(block))); + + int trailer_result = + adapter->SubmitTrailer(1, ToHeaders({{":final-status", "a-ok"}})); + ASSERT_EQ(trailer_result, 0); + EXPECT_TRUE(adapter->want_write()); + + EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x4)); + EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x4, 0)); + + EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, + END_STREAM_FLAG | END_HEADERS_FLAG)); + EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, + END_STREAM_FLAG | END_HEADERS_FLAG, 0)); + + EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); + + send_result = adapter->Send(); + EXPECT_EQ(0, send_result); + EXPECT_THAT(visitor.data(), + EqualsFrames({static_cast<SpdyFrameType>(kMetadataFrameType), + SpdyFrameType::HEADERS})); +} + +} // namespace +} // namespace test +} // namespace adapter +} // namespace http2
diff --git a/quiche/http2/adapter/oghttp2_adapter_test.cc b/quiche/http2/adapter/oghttp2_adapter_test.cc index 4c9ba14..1d52d28 100644 --- a/quiche/http2/adapter/oghttp2_adapter_test.cc +++ b/quiche/http2/adapter/oghttp2_adapter_test.cc
@@ -1441,315 +1441,6 @@ EXPECT_THAT(data, EqualsFrames({SpdyFrameType::HEADERS})); } -TEST(OgHttp2AdapterTest, ClientHandlesMetadata) { - DataSavingVisitor visitor; - OgHttp2Adapter::Options options; - options.perspective = Perspective::kClient; - auto adapter = OgHttp2Adapter::Create(visitor, options); - - testing::InSequence s; - - const std::vector<Header> headers1 = - ToHeaders({{":method", "GET"}, - {":scheme", "http"}, - {":authority", "example.com"}, - {":path", "/this/is/request/one"}}); - - const char* kSentinel1 = "arbitrary pointer 1"; - const int32_t stream_id1 = - adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1)); - ASSERT_GT(stream_id1, 0); - QUICHE_LOG(INFO) << "Created stream: " << stream_id1; - - EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); - EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); - EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, - END_STREAM_FLAG | END_HEADERS_FLAG)); - EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, - END_STREAM_FLAG | END_HEADERS_FLAG, 0)); - - int result = adapter->Send(); - EXPECT_EQ(0, result); - absl::string_view data = visitor.data(); - EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); - data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); - EXPECT_THAT(data, - EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS})); - visitor.Clear(); - - const std::string stream_frames = - TestFrameSequence() - .ServerPreface() - .Metadata(0, "Example connection metadata") - .Headers(1, - {{":status", "200"}, - {"server", "my-fake-server"}, - {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, - /*fin=*/false) - .Metadata(1, "Example stream metadata") - .Data(1, "This is the response body.", true) - .Serialize(); - - // Server preface (empty SETTINGS) - EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); - EXPECT_CALL(visitor, OnSettingsStart()); - EXPECT_CALL(visitor, OnSettingsEnd()); - - EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 4)); - EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _)); - EXPECT_CALL(visitor, OnMetadataForStream(0, _)); - EXPECT_CALL(visitor, OnMetadataEndForStream(0)); - EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); - EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); - EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200")); - EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server")); - EXPECT_CALL(visitor, - OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT")); - EXPECT_CALL(visitor, OnEndHeadersForStream(1)); - EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 4)); - EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _)); - EXPECT_CALL(visitor, OnMetadataForStream(1, _)); - EXPECT_CALL(visitor, OnMetadataEndForStream(1)); - EXPECT_CALL(visitor, OnFrameHeader(1, 26, DATA, 1)); - EXPECT_CALL(visitor, OnBeginDataForStream(1, 26)); - EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body.")); - EXPECT_CALL(visitor, OnEndStream(1)); - EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); - - const int64_t stream_result = adapter->ProcessBytes(stream_frames); - EXPECT_EQ(stream_frames.size(), static_cast<size_t>(stream_result)); - - EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, ACK_FLAG)); - EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, ACK_FLAG, 0)); - - EXPECT_TRUE(adapter->want_write()); - result = adapter->Send(); - EXPECT_EQ(0, result); - EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS})); -} - -TEST(OgHttp2AdapterTest, ClientHandlesMetadataWithEmptyPayload) { - DataSavingVisitor visitor; - OgHttp2Adapter::Options options; - options.perspective = Perspective::kClient; - auto adapter = OgHttp2Adapter::Create(visitor, options); - - testing::InSequence s; - - const std::vector<Header> headers1 = - ToHeaders({{":method", "GET"}, - {":scheme", "http"}, - {":authority", "example.com"}, - {":path", "/this/is/request/one"}}); - - const int32_t stream_id = adapter->SubmitRequest(headers1, nullptr, nullptr); - ASSERT_GT(stream_id, 0); - - EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); - EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); - EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, - END_STREAM_FLAG | END_HEADERS_FLAG)); - EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, - END_STREAM_FLAG | END_HEADERS_FLAG, 0)); - - int result = adapter->Send(); - EXPECT_EQ(0, result); - absl::string_view data = visitor.data(); - EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); - data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); - EXPECT_THAT(data, - EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS})); - visitor.Clear(); - - const std::string stream_frames = - TestFrameSequence() - .ServerPreface() - .Headers(1, - {{":status", "200"}, - {"server", "my-fake-server"}, - {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, - /*fin=*/false) - .Metadata(1, "") - .Data(1, "This is the response body.", true) - .Serialize(); - - // Server 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, _, _)).Times(3); - EXPECT_CALL(visitor, OnEndHeadersForStream(1)); - EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 4)); - EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _)); - EXPECT_CALL(visitor, OnMetadataEndForStream(1)); - EXPECT_CALL(visitor, OnFrameHeader(1, _, DATA, 1)); - EXPECT_CALL(visitor, OnBeginDataForStream(1, _)); - EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body.")); - EXPECT_CALL(visitor, OnEndStream(1)); - EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); - - const int64_t stream_result = adapter->ProcessBytes(stream_frames); - EXPECT_EQ(stream_frames.size(), static_cast<size_t>(stream_result)); -} - -TEST(OgHttp2AdapterTest, ClientHandlesMetadataWithPayloadError) { - DataSavingVisitor visitor; - OgHttp2Adapter::Options options; - options.perspective = Perspective::kClient; - auto adapter = OgHttp2Adapter::Create(visitor, options); - - testing::InSequence s; - - const std::vector<Header> headers = - ToHeaders({{":method", "GET"}, - {":scheme", "http"}, - {":authority", "example.com"}, - {":path", "/this/is/request/one"}}); - - const int32_t stream_id = adapter->SubmitRequest(headers, nullptr, nullptr); - ASSERT_GT(stream_id, 0); - - EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); - EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); - EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, - END_STREAM_FLAG | END_HEADERS_FLAG)); - EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, - END_STREAM_FLAG | END_HEADERS_FLAG, 0)); - - int result = adapter->Send(); - EXPECT_EQ(0, result); - visitor.Clear(); - - const std::string stream_frames = - TestFrameSequence() - .ServerPreface() - .Metadata(0, "Example connection metadata") - .Headers(stream_id, - {{":status", "200"}, - {"server", "my-fake-server"}, - {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, - /*fin=*/false) - .Metadata(stream_id, "Example stream metadata") - .Data(stream_id, "This is the response body.", true) - .Serialize(); - - // Server preface (empty SETTINGS) - EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); - EXPECT_CALL(visitor, OnSettingsStart()); - EXPECT_CALL(visitor, OnSettingsEnd()); - - EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 4)); - EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _)); - EXPECT_CALL(visitor, OnMetadataForStream(0, _)); - EXPECT_CALL(visitor, OnMetadataEndForStream(0)); - EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, HEADERS, 4)); - EXPECT_CALL(visitor, OnBeginHeadersForStream(stream_id)); - EXPECT_CALL(visitor, OnHeaderForStream(stream_id, _, _)).Times(3); - EXPECT_CALL(visitor, OnEndHeadersForStream(stream_id)); - EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, kMetadataFrameType, 4)); - EXPECT_CALL(visitor, OnBeginMetadataForStream(stream_id, _)); - EXPECT_CALL(visitor, OnMetadataForStream(stream_id, _)) - .WillOnce(testing::Return(false)); - // Remaining frames are not processed due to the error. - EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kParseError)); - - const int64_t stream_result = adapter->ProcessBytes(stream_frames); - // Negative integer returned to indicate an error. - EXPECT_LT(stream_result, 0); - - EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); - EXPECT_CALL(visitor, - OnFrameSent(GOAWAY, 0, _, 0x0, - static_cast<int>(Http2ErrorCode::INTERNAL_ERROR))); - - EXPECT_FALSE(adapter->want_read()); - EXPECT_TRUE(adapter->want_write()); - result = adapter->Send(); - EXPECT_EQ(0, result); - EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::GOAWAY})); -} - -TEST(OgHttp2AdapterTest, ClientHandlesMetadataWithCompletionError) { - DataSavingVisitor visitor; - OgHttp2Adapter::Options options; - options.perspective = Perspective::kClient; - auto adapter = OgHttp2Adapter::Create(visitor, options); - - testing::InSequence s; - - const std::vector<Header> headers = - ToHeaders({{":method", "GET"}, - {":scheme", "http"}, - {":authority", "example.com"}, - {":path", "/this/is/request/one"}}); - - const int32_t stream_id = adapter->SubmitRequest(headers, nullptr, nullptr); - ASSERT_GT(stream_id, 0); - - EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); - EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); - EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, - END_STREAM_FLAG | END_HEADERS_FLAG)); - EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, - END_STREAM_FLAG | END_HEADERS_FLAG, 0)); - - int result = adapter->Send(); - EXPECT_EQ(0, result); - visitor.Clear(); - - const std::string stream_frames = - TestFrameSequence() - .ServerPreface() - .Metadata(0, "Example connection metadata") - .Headers(stream_id, - {{":status", "200"}, - {"server", "my-fake-server"}, - {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, - /*fin=*/false) - .Metadata(stream_id, "Example stream metadata") - .Data(stream_id, "This is the response body.", true) - .Serialize(); - - // Server preface (empty SETTINGS) - EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); - EXPECT_CALL(visitor, OnSettingsStart()); - EXPECT_CALL(visitor, OnSettingsEnd()); - - EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 4)); - EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _)); - EXPECT_CALL(visitor, OnMetadataForStream(0, _)); - EXPECT_CALL(visitor, OnMetadataEndForStream(0)); - EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, HEADERS, 4)); - EXPECT_CALL(visitor, OnBeginHeadersForStream(stream_id)); - EXPECT_CALL(visitor, OnHeaderForStream(stream_id, _, _)).Times(3); - EXPECT_CALL(visitor, OnEndHeadersForStream(stream_id)); - EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, kMetadataFrameType, 4)); - EXPECT_CALL(visitor, OnBeginMetadataForStream(stream_id, _)); - EXPECT_CALL(visitor, OnMetadataForStream(stream_id, _)); - EXPECT_CALL(visitor, OnMetadataEndForStream(stream_id)) - .WillOnce(testing::Return(false)); - // Remaining frames are not processed due to the error. - EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kParseError)); - - const int64_t stream_result = adapter->ProcessBytes(stream_frames); - // Negative integer returned to indicate an error. - EXPECT_LT(stream_result, 0); - - EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); - EXPECT_CALL(visitor, - OnFrameSent(GOAWAY, 0, _, 0x0, - static_cast<int>(Http2ErrorCode::INTERNAL_ERROR))); - - EXPECT_FALSE(adapter->want_read()); - EXPECT_TRUE(adapter->want_write()); - result = adapter->Send(); - EXPECT_EQ(0, result); - EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::GOAWAY})); -} - TEST(OgHttp2AdapterTest, ClientRstStreamWhileHandlingHeaders) { DataSavingVisitor visitor; OgHttp2Adapter::Options options; @@ -3877,52 +3568,6 @@ EXPECT_EQ(0, result); } -TEST(OgHttp2AdapterTest, ClientSendsMetadataAfterFlowControlBlock) { - DataSavingVisitor visitor; - OgHttp2Adapter::Options options; - options.perspective = Perspective::kClient; - auto adapter = OgHttp2Adapter::Create(visitor, options); - - testing::InSequence s; - - const std::vector<Header> headers1 = - ToHeaders({{":method", "GET"}, - {":scheme", "http"}, - {":authority", "example.com"}, - {":path", "/this/is/request/one"}}); - - const std::string kBody = std::string(100 * 1024, 'a'); - auto body1 = std::make_unique<TestDataFrameSource>(visitor, false); - body1->AppendPayload(kBody); - body1->EndData(); - - const int32_t stream_id1 = - adapter->SubmitRequest(headers1, std::move(body1), nullptr); - ASSERT_GT(stream_id1, 0); - - EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); - EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); - EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x4)); - EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x4, 0)); - // 4 DATA frames should saturate the default 64kB stream/connection flow - // control window. - EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id1, _, 0x0, 0)).Times(4); - - int result = adapter->Send(); - EXPECT_EQ(0, result); - EXPECT_FALSE(adapter->want_write()); - EXPECT_EQ(0, adapter->GetSendWindowSize()); - - auto source = std::make_unique<TestMetadataSource>(ToHeaderBlock(ToHeaders( - {{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}}))); - adapter->SubmitMetadata(1, 16384u, std::move(source)); - EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x4)); - EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x4, 0)); - - result = adapter->Send(); - EXPECT_EQ(0, result); -} - TEST(OgHttp2AdapterTest, ClientQueuesRequests) { DataSavingVisitor visitor; OgHttp2Adapter::Options options; @@ -4067,90 +3712,6 @@ adapter->Send(); } -TEST(OgHttp2AdapterTest, SubmitMetadata) { - DataSavingVisitor visitor; - OgHttp2Adapter::Options options; - options.perspective = Perspective::kServer; - auto adapter = OgHttp2Adapter::Create(visitor, options); - - auto source = std::make_unique<TestMetadataSource>(ToHeaderBlock(ToHeaders( - {{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}}))); - adapter->SubmitMetadata(1, 16384u, std::move(source)); - EXPECT_TRUE(adapter->want_write()); - - EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); - EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); - EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x4)); - EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x4, 0)); - - int result = adapter->Send(); - EXPECT_EQ(0, result); - EXPECT_THAT(visitor.data(), - EqualsFrames({SpdyFrameType::SETTINGS, - static_cast<SpdyFrameType>(kMetadataFrameType)})); - EXPECT_FALSE(adapter->want_write()); -} - -TEST(OgHttp2AdapterTest, SubmitMetadataMultipleFrames) { - DataSavingVisitor visitor; - OgHttp2Adapter::Options options; - options.perspective = Perspective::kServer; - auto adapter = OgHttp2Adapter::Create(visitor, options); - - const auto kLargeValue = std::string(63 * 1024, 'a'); - auto source = std::make_unique<TestMetadataSource>( - ToHeaderBlock(ToHeaders({{"large-value", kLargeValue}}))); - adapter->SubmitMetadata(1, 16384u, std::move(source)); - EXPECT_TRUE(adapter->want_write()); - - testing::InSequence seq; - EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); - EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); - EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x0)); - EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x0, 0)); - EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x0)); - EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x0, 0)); - EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x0)); - EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x0, 0)); - EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x4)); - EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x4, 0)); - - int result = adapter->Send(); - EXPECT_EQ(0, result); - absl::string_view serialized = visitor.data(); - EXPECT_THAT(serialized, - EqualsFrames({SpdyFrameType::SETTINGS, - static_cast<SpdyFrameType>(kMetadataFrameType), - static_cast<SpdyFrameType>(kMetadataFrameType), - static_cast<SpdyFrameType>(kMetadataFrameType), - static_cast<SpdyFrameType>(kMetadataFrameType)})); - EXPECT_FALSE(adapter->want_write()); -} - -TEST(OgHttp2AdapterTest, SubmitConnectionMetadata) { - DataSavingVisitor visitor; - OgHttp2Adapter::Options options; - options.perspective = Perspective::kServer; - auto adapter = OgHttp2Adapter::Create(visitor, options); - - auto source = std::make_unique<TestMetadataSource>(ToHeaderBlock(ToHeaders( - {{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}}))); - adapter->SubmitMetadata(0, 16384u, std::move(source)); - EXPECT_TRUE(adapter->want_write()); - - EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); - EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); - EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 0, _, 0x4)); - EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 0, _, 0x4, 0)); - - int result = adapter->Send(); - EXPECT_EQ(0, result); - EXPECT_THAT(visitor.data(), - EqualsFrames({SpdyFrameType::SETTINGS, - static_cast<SpdyFrameType>(kMetadataFrameType)})); - EXPECT_FALSE(adapter->want_write()); -} - TEST(OgHttp2AdapterTest, GetSendWindowSize) { DataSavingVisitor visitor; OgHttp2Adapter::Options options; @@ -4704,69 +4265,6 @@ EXPECT_EQ(frames.size(), static_cast<size_t>(result)); } -TEST(OgHttp2AdapterTest, ClientSendsMetadataWithContinuation) { - DataSavingVisitor visitor; - OgHttp2Adapter::Options options; - options.perspective = Perspective::kServer; - auto adapter = OgHttp2Adapter::Create(visitor, options); - EXPECT_FALSE(adapter->want_write()); - - const std::string frames = - TestFrameSequence() - .ClientPreface() - .Metadata(0, "Example connection metadata in multiple frames", true) - .Headers(1, - {{":method", "GET"}, - {":scheme", "https"}, - {":authority", "example.com"}, - {":path", "/this/is/request/one"}}, - /*fin=*/false, - /*add_continuation=*/true) - .Metadata(1, - "Some stream metadata that's also sent in multiple frames", - true) - .Serialize(); - testing::InSequence s; - - // Client preface (empty SETTINGS) - EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); - EXPECT_CALL(visitor, OnSettingsStart()); - EXPECT_CALL(visitor, OnSettingsEnd()); - // Metadata on stream 0 - EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 0)); - EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _)); - EXPECT_CALL(visitor, OnMetadataForStream(0, _)); - EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 4)); - EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _)); - EXPECT_CALL(visitor, OnMetadataForStream(0, _)); - EXPECT_CALL(visitor, OnMetadataEndForStream(0)); - - // Stream 1 - EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 0)); - EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); - EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "GET")); - EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https")); - EXPECT_CALL(visitor, OnFrameHeader(1, _, CONTINUATION, 4)); - EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com")); - EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one")); - EXPECT_CALL(visitor, OnEndHeadersForStream(1)); - // Metadata on stream 1 - EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 0)); - EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _)); - EXPECT_CALL(visitor, OnMetadataForStream(1, _)); - EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 4)); - EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _)); - EXPECT_CALL(visitor, OnMetadataForStream(1, _)); - EXPECT_CALL(visitor, OnMetadataEndForStream(1)); - - const int64_t result = adapter->ProcessBytes(frames); - EXPECT_EQ(frames.size(), static_cast<size_t>(result)); - EXPECT_EQ("Example connection metadata in multiple frames", - absl::StrJoin(visitor.GetMetadata(0), "")); - EXPECT_EQ("Some stream metadata that's also sent in multiple frames", - absl::StrJoin(visitor.GetMetadata(1), "")); -} - TEST(OgHttp2AdapterTest, RepeatedHeaderNames) { DataSavingVisitor visitor; OgHttp2Adapter::Options options; @@ -5351,95 +4849,6 @@ EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::HEADERS})); } -TEST(OgHttp2AdapterTest, ServerQueuesMetadataThenTrailers) { - DataSavingVisitor visitor; - OgHttp2Adapter::Options options; - options.perspective = Perspective::kServer; - auto adapter = OgHttp2Adapter::Create(visitor, options); - EXPECT_FALSE(adapter->want_write()); - - const std::string frames = TestFrameSequence() - .ClientPreface() - .Headers(1, - {{":method", "GET"}, - {":scheme", "https"}, - {":authority", "example.com"}, - {":path", "/"}}, - /*fin=*/true) - .Serialize(); - testing::InSequence s; - - // Client preface (empty SETTINGS) - EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); - EXPECT_CALL(visitor, OnSettingsStart()); - EXPECT_CALL(visitor, OnSettingsEnd()); - // Stream 1 - EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5)); - EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); - EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(4); - EXPECT_CALL(visitor, OnEndHeadersForStream(1)); - EXPECT_CALL(visitor, OnEndStream(1)); - - const int64_t result = adapter->ProcessBytes(frames); - EXPECT_EQ(frames.size(), static_cast<size_t>(result)); - - 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 = std::make_unique<TestDataFrameSource>(visitor, false); - body1->AppendPayload(kBody); - body1->EndData(); - 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(SETTINGS, 0, _, 0x0)); - EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); - EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, ACK_FLAG)); - EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, ACK_FLAG, 0)); - EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x4)); - EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x4, 0)); - EXPECT_CALL(visitor, OnFrameSent(DATA, 1, _, 0x0, 0)); - - int send_result = adapter->Send(); - EXPECT_EQ(0, send_result); - EXPECT_THAT(visitor.data(), - EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, - SpdyFrameType::HEADERS, SpdyFrameType::DATA})); - EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody)); - visitor.Clear(); - EXPECT_FALSE(adapter->want_write()); - - spdy::Http2HeaderBlock block; - block["key"] = "wild value!"; - adapter->SubmitMetadata( - 1, 16384u, std::make_unique<TestMetadataSource>(std::move(block))); - - int trailer_result = - adapter->SubmitTrailer(1, ToHeaders({{":final-status", "a-ok"}})); - ASSERT_EQ(trailer_result, 0); - EXPECT_TRUE(adapter->want_write()); - - EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x4)); - EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x4, 0)); - - EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, - END_STREAM_FLAG | END_HEADERS_FLAG)); - EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, - END_STREAM_FLAG | END_HEADERS_FLAG, 0)); - - EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); - - send_result = adapter->Send(); - EXPECT_EQ(0, send_result); - EXPECT_THAT(visitor.data(), - EqualsFrames({static_cast<SpdyFrameType>(kMetadataFrameType), - SpdyFrameType::HEADERS})); -} - TEST(OgHttp2AdapterTest, ServerHandlesDataWithPadding) { DataSavingVisitor visitor; OgHttp2Adapter::Options options;