|  | #include <cstdint> | 
|  | #include <memory> | 
|  | #include <string> | 
|  | #include <utility> | 
|  | #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_adapter.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/http/http_header_block.h" | 
|  | #include "quiche/common/platform/api/quiche_expect_bug.h" | 
|  | #include "quiche/common/platform/api/quiche_test.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) { | 
|  | TestVisitor 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, true, 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) { | 
|  | TestVisitor 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, true, 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) { | 
|  | TestVisitor 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, true, 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) { | 
|  | TestVisitor 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, true, 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})); | 
|  | } | 
|  |  | 
|  | class MetadataApiTest : public quiche::test::QuicheTestWithParam<bool> {}; | 
|  |  | 
|  | INSTANTIATE_TEST_SUITE_P(WithAndWithoutNewApi, MetadataApiTest, | 
|  | testing::Bool()); | 
|  |  | 
|  | TEST_P(MetadataApiTest, ClientSendsMetadataAfterFlowControlBlock) { | 
|  | TestVisitor 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'); | 
|  | visitor.AppendPayloadForStream(1, kBody); | 
|  | visitor.SetEndData(1, false); | 
|  | auto body1 = std::make_unique<VisitorDataSource>(visitor, 1); | 
|  |  | 
|  | const int32_t stream_id1 = | 
|  | adapter->SubmitRequest(headers1, std::move(body1), false, nullptr); | 
|  | ASSERT_EQ(stream_id1, 1); | 
|  |  | 
|  | 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()); | 
|  |  | 
|  | const quiche::HttpHeaderBlock block = ToHeaderBlock(ToHeaders( | 
|  | {{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}})); | 
|  | if (GetParam()) { | 
|  | visitor.AppendMetadataForStream(stream_id1, block); | 
|  | adapter->SubmitMetadata(stream_id1, 1); | 
|  | } else { | 
|  | auto source = std::make_unique<TestMetadataSource>(block); | 
|  | 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) { | 
|  | TestVisitor 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_P(MetadataApiTest, SubmitMetadata) { | 
|  | TestVisitor visitor; | 
|  | OgHttp2Adapter::Options options; | 
|  | options.perspective = Perspective::kServer; | 
|  | auto adapter = OgHttp2Adapter::Create(visitor, options); | 
|  |  | 
|  | const quiche::HttpHeaderBlock block = ToHeaderBlock(ToHeaders( | 
|  | {{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}})); | 
|  | if (GetParam()) { | 
|  | visitor.AppendMetadataForStream(1, block); | 
|  | adapter->SubmitMetadata(1, 1); | 
|  | } else { | 
|  | auto source = std::make_unique<TestMetadataSource>(block); | 
|  | 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()); | 
|  | } | 
|  |  | 
|  | size_t DivRoundUp(size_t numerator, size_t denominator) { | 
|  | return numerator / denominator + (numerator % denominator == 0 ? 0 : 1); | 
|  | } | 
|  |  | 
|  | TEST_P(MetadataApiTest, SubmitMetadataMultipleFrames) { | 
|  | TestVisitor visitor; | 
|  | OgHttp2Adapter::Options options; | 
|  | options.perspective = Perspective::kServer; | 
|  | auto adapter = OgHttp2Adapter::Create(visitor, options); | 
|  |  | 
|  | const auto kLargeValue = std::string(63 * 1024, 'a'); | 
|  | const quiche::HttpHeaderBlock block = | 
|  | ToHeaderBlock(ToHeaders({{"large-value", kLargeValue}})); | 
|  | if (GetParam()) { | 
|  | visitor.AppendMetadataForStream(1, block); | 
|  | adapter->SubmitMetadata(1, DivRoundUp(kLargeValue.size(), 16384u)); | 
|  | } else { | 
|  | auto source = std::make_unique<TestMetadataSource>(block); | 
|  | 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_P(MetadataApiTest, SubmitConnectionMetadata) { | 
|  | TestVisitor visitor; | 
|  | OgHttp2Adapter::Options options; | 
|  | options.perspective = Perspective::kServer; | 
|  | auto adapter = OgHttp2Adapter::Create(visitor, options); | 
|  |  | 
|  | const quiche::HttpHeaderBlock block = ToHeaderBlock(ToHeaders( | 
|  | {{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}})); | 
|  | if (GetParam()) { | 
|  | visitor.AppendMetadataForStream(0, block); | 
|  | adapter->SubmitMetadata(0, 1); | 
|  | } else { | 
|  | auto source = std::make_unique<TestMetadataSource>(block); | 
|  | 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_P(MetadataApiTest, ServerQueuesMetadataThenTrailers) { | 
|  | TestVisitor 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. | 
|  | visitor.AppendPayloadForStream(1, kBody); | 
|  | visitor.SetEndData(1, false); | 
|  | auto body1 = std::make_unique<VisitorDataSource>(visitor, 1); | 
|  | int submit_result = adapter->SubmitResponse( | 
|  | 1, ToHeaders({{":status", "200"}, {"x-comment", "Sure, sounds good."}}), | 
|  | std::move(body1), false); | 
|  | 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()); | 
|  |  | 
|  | const quiche::HttpHeaderBlock block = | 
|  | ToHeaderBlock(ToHeaders({{"key", "wild value!"}})); | 
|  | if (GetParam()) { | 
|  | visitor.AppendMetadataForStream(1, block); | 
|  | adapter->SubmitMetadata(1, 1); | 
|  | } else { | 
|  | 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 |