|  | #include "http2/adapter/nghttp2_adapter.h" | 
|  |  | 
|  | #include "http2/adapter/http2_protocol.h" | 
|  | #include "http2/adapter/http2_visitor_interface.h" | 
|  | #include "http2/adapter/mock_http2_visitor.h" | 
|  | #include "http2/adapter/nghttp2_test_utils.h" | 
|  | #include "http2/adapter/oghttp2_util.h" | 
|  | #include "http2/adapter/test_frame_sequence.h" | 
|  | #include "http2/adapter/test_utils.h" | 
|  | #include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h" | 
|  | #include "common/platform/api/quiche_test.h" | 
|  |  | 
|  | namespace http2 { | 
|  | namespace adapter { | 
|  | namespace test { | 
|  | namespace { | 
|  |  | 
|  | using ConnectionError = Http2VisitorInterface::ConnectionError; | 
|  |  | 
|  | using testing::_; | 
|  |  | 
|  | enum FrameType { | 
|  | DATA, | 
|  | HEADERS, | 
|  | PRIORITY, | 
|  | RST_STREAM, | 
|  | SETTINGS, | 
|  | PUSH_PROMISE, | 
|  | PING, | 
|  | GOAWAY, | 
|  | WINDOW_UPDATE, | 
|  | CONTINUATION, | 
|  | }; | 
|  |  | 
|  | // This send callback assumes |source|'s pointer is a TestDataSource, and | 
|  | // |user_data| is a Http2VisitorInterface. | 
|  | int TestSendCallback(nghttp2_session*, nghttp2_frame* /*frame*/, | 
|  | const uint8_t* framehd, size_t length, | 
|  | nghttp2_data_source* source, void* user_data) { | 
|  | auto* visitor = static_cast<Http2VisitorInterface*>(user_data); | 
|  | // Send the frame header via the visitor. | 
|  | ssize_t result = visitor->OnReadyToSend(ToStringView(framehd, 9)); | 
|  | if (result == 0) { | 
|  | return NGHTTP2_ERR_WOULDBLOCK; | 
|  | } | 
|  | auto* test_source = static_cast<TestDataSource*>(source->ptr); | 
|  | absl::string_view payload = test_source->ReadNext(length); | 
|  | // Send the frame payload via the visitor. | 
|  | visitor->OnReadyToSend(payload); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ClientConstruction) { | 
|  | testing::StrictMock<MockHttp2Visitor> visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  | ASSERT_NE(nullptr, adapter); | 
|  | EXPECT_TRUE(adapter->want_read()); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  | EXPECT_FALSE(adapter->IsServerSession()); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ClientHandlesFrames) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  | int result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | EXPECT_THAT(visitor.data(), | 
|  | testing::StrEq(spdy::kHttp2ConnectionHeaderPrefix)); | 
|  | visitor.Clear(); | 
|  |  | 
|  | EXPECT_EQ(0, adapter->GetHighestReceivedStreamId()); | 
|  |  | 
|  | const std::string initial_frames = TestFrameSequence() | 
|  | .ServerPreface() | 
|  | .Ping(42) | 
|  | .WindowUpdate(0, 1000) | 
|  | .Serialize(); | 
|  | testing::InSequence s; | 
|  |  | 
|  | // 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, 8, PING, 0)); | 
|  | EXPECT_CALL(visitor, OnPing(42, false)); | 
|  | EXPECT_CALL(visitor, OnFrameHeader(0, 4, WINDOW_UPDATE, 0)); | 
|  | EXPECT_CALL(visitor, OnWindowUpdate(0, 1000)); | 
|  |  | 
|  | const int64_t initial_result = adapter->ProcessBytes(initial_frames); | 
|  | EXPECT_EQ(initial_frames.size(), initial_result); | 
|  |  | 
|  | EXPECT_EQ(adapter->GetSendWindowSize(), kInitialFlowControlWindowSize + 1000); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(PING, 0, 8, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(PING, 0, 8, 0x1, 0)); | 
|  |  | 
|  | result = adapter->Send(); | 
|  | // Some bytes should have been serialized. | 
|  | EXPECT_EQ(0, result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS, | 
|  | spdy::SpdyFrameType::PING})); | 
|  | visitor.Clear(); | 
|  |  | 
|  | const std::vector<const Header> headers1 = | 
|  | ToHeaders({{":method", "GET"}, | 
|  | {":scheme", "http"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/one"}}); | 
|  |  | 
|  | const std::vector<const Header> headers2 = | 
|  | ToHeaders({{":method", "GET"}, | 
|  | {":scheme", "http"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/two"}}); | 
|  |  | 
|  | const std::vector<const Header> headers3 = | 
|  | ToHeaders({{":method", "GET"}, | 
|  | {":scheme", "http"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/three"}}); | 
|  |  | 
|  | const char* kSentinel1 = "arbitrary pointer 1"; | 
|  | const char* kSentinel3 = "arbitrary pointer 3"; | 
|  | 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; | 
|  |  | 
|  | const int32_t stream_id2 = adapter->SubmitRequest(headers2, nullptr, nullptr); | 
|  | ASSERT_GT(stream_id2, 0); | 
|  | QUICHE_LOG(INFO) << "Created stream: " << stream_id2; | 
|  |  | 
|  | const int32_t stream_id3 = | 
|  | adapter->SubmitRequest(headers3, nullptr, const_cast<char*>(kSentinel3)); | 
|  | ASSERT_GT(stream_id3, 0); | 
|  | QUICHE_LOG(INFO) << "Created stream: " << stream_id3; | 
|  |  | 
|  | const char* kSentinel2 = "arbitrary pointer 2"; | 
|  | adapter->SetStreamUserData(stream_id2, const_cast<char*>(kSentinel2)); | 
|  | adapter->SetStreamUserData(stream_id3, nullptr); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0)); | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id2, _, 0x5)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id2, _, 0x5, 0)); | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id3, _, 0x5)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id3, _, 0x5, 0)); | 
|  |  | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS, | 
|  | spdy::SpdyFrameType::HEADERS, | 
|  | spdy::SpdyFrameType::HEADERS})); | 
|  | visitor.Clear(); | 
|  |  | 
|  | // All streams are active and have not yet received any data, so the receive | 
|  | // window should be at the initial value. | 
|  | EXPECT_EQ(kInitialFlowControlWindowSize, | 
|  | adapter->GetStreamReceiveWindowSize(stream_id1)); | 
|  | EXPECT_EQ(kInitialFlowControlWindowSize, | 
|  | adapter->GetStreamReceiveWindowSize(stream_id2)); | 
|  | EXPECT_EQ(kInitialFlowControlWindowSize, | 
|  | adapter->GetStreamReceiveWindowSize(stream_id3)); | 
|  |  | 
|  | // Upper bound on the flow control receive window should be the initial value. | 
|  | EXPECT_EQ(kInitialFlowControlWindowSize, | 
|  | adapter->GetStreamReceiveWindowLimit(stream_id1)); | 
|  |  | 
|  | // Connection has not yet received any data. | 
|  | EXPECT_EQ(kInitialFlowControlWindowSize, adapter->GetReceiveWindowSize()); | 
|  |  | 
|  | EXPECT_EQ(0, adapter->GetHighestReceivedStreamId()); | 
|  |  | 
|  | EXPECT_EQ(kSentinel1, adapter->GetStreamUserData(stream_id1)); | 
|  | EXPECT_EQ(kSentinel2, adapter->GetStreamUserData(stream_id2)); | 
|  | EXPECT_EQ(nullptr, adapter->GetStreamUserData(stream_id3)); | 
|  |  | 
|  | EXPECT_EQ(0, adapter->GetHpackDecoderDynamicTableSize()); | 
|  |  | 
|  | const std::string stream_frames = | 
|  | TestFrameSequence() | 
|  | .Headers(1, | 
|  | {{":status", "200"}, | 
|  | {"server", "my-fake-server"}, | 
|  | {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, | 
|  | /*fin=*/false) | 
|  | .Data(1, "This is the response body.") | 
|  | .RstStream(3, Http2ErrorCode::INTERNAL_ERROR) | 
|  | .GoAway(5, Http2ErrorCode::ENHANCE_YOUR_CALM, "calm down!!") | 
|  | .Serialize(); | 
|  |  | 
|  | 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, 26, DATA, 0)); | 
|  | EXPECT_CALL(visitor, OnBeginDataForStream(1, 26)); | 
|  | EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body.")); | 
|  | EXPECT_CALL(visitor, OnFrameHeader(3, 4, RST_STREAM, 0)); | 
|  | EXPECT_CALL(visitor, OnRstStream(3, Http2ErrorCode::INTERNAL_ERROR)); | 
|  | EXPECT_CALL(visitor, OnCloseStream(3, Http2ErrorCode::INTERNAL_ERROR)); | 
|  | EXPECT_CALL(visitor, OnFrameHeader(0, 19, GOAWAY, 0)); | 
|  | EXPECT_CALL(visitor, | 
|  | OnGoAway(5, Http2ErrorCode::ENHANCE_YOUR_CALM, "calm down!!")); | 
|  | const int64_t stream_result = adapter->ProcessBytes(stream_frames); | 
|  | EXPECT_EQ(stream_frames.size(), stream_result); | 
|  |  | 
|  | // First stream has received some data. | 
|  | EXPECT_GT(kInitialFlowControlWindowSize, | 
|  | adapter->GetStreamReceiveWindowSize(stream_id1)); | 
|  | // Second stream was closed. | 
|  | EXPECT_EQ(-1, adapter->GetStreamReceiveWindowSize(stream_id2)); | 
|  | // Third stream has not received any data. | 
|  | EXPECT_EQ(kInitialFlowControlWindowSize, | 
|  | adapter->GetStreamReceiveWindowSize(stream_id3)); | 
|  |  | 
|  | // Connection window should be the same as the first stream. | 
|  | EXPECT_EQ(adapter->GetReceiveWindowSize(), | 
|  | adapter->GetStreamReceiveWindowSize(stream_id1)); | 
|  |  | 
|  | // Upper bound on the flow control receive window should still be the initial | 
|  | // value. | 
|  | EXPECT_EQ(kInitialFlowControlWindowSize, | 
|  | adapter->GetStreamReceiveWindowLimit(stream_id1)); | 
|  |  | 
|  | EXPECT_GT(adapter->GetHpackDecoderDynamicTableSize(), 0); | 
|  |  | 
|  | // Should be 3, but this method only works for server adapters. | 
|  | EXPECT_EQ(0, adapter->GetHighestReceivedStreamId()); | 
|  |  | 
|  | // Even though the client recieved a GOAWAY, streams 1 and 5 are still active. | 
|  | EXPECT_TRUE(adapter->want_read()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnFrameHeader(1, 0, DATA, 1)); | 
|  | EXPECT_CALL(visitor, OnBeginDataForStream(1, 0)); | 
|  | EXPECT_CALL(visitor, OnEndStream(1)); | 
|  | EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR)); | 
|  | EXPECT_CALL(visitor, OnFrameHeader(5, 4, RST_STREAM, 0)); | 
|  | EXPECT_CALL(visitor, OnRstStream(5, Http2ErrorCode::REFUSED_STREAM)); | 
|  | EXPECT_CALL(visitor, OnCloseStream(5, Http2ErrorCode::REFUSED_STREAM)); | 
|  | adapter->ProcessBytes(TestFrameSequence() | 
|  | .Data(1, "", true) | 
|  | .RstStream(5, Http2ErrorCode::REFUSED_STREAM) | 
|  | .Serialize()); | 
|  |  | 
|  | // Should be 5, but this method only works for server adapters. | 
|  | EXPECT_EQ(0, adapter->GetHighestReceivedStreamId()); | 
|  |  | 
|  | // After receiving END_STREAM for 1 and RST_STREAM for 5, the session no | 
|  | // longer expects reads. | 
|  | EXPECT_FALSE(adapter->want_read()); | 
|  |  | 
|  | // Client will not have anything else to write. | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | EXPECT_THAT(visitor.data(), testing::IsEmpty()); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ClientHandlesTrailers) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  |  | 
|  | testing::InSequence s; | 
|  |  | 
|  | const std::vector<const 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(HEADERS, stream_id1, _, 0x5)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 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({spdy::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) | 
|  | .Data(1, "This is the response body.") | 
|  | .Headers(1, {{"final-status", "A-OK"}}, | 
|  | /*fin=*/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, ":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, 26, DATA, 0)); | 
|  | EXPECT_CALL(visitor, OnBeginDataForStream(1, 26)); | 
|  | EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body.")); | 
|  | EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5)); | 
|  | EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); | 
|  | EXPECT_CALL(visitor, OnHeaderForStream(1, "final-status", "A-OK")); | 
|  | EXPECT_CALL(visitor, OnEndHeadersForStream(1)); | 
|  | EXPECT_CALL(visitor, OnEndStream(1)); | 
|  | EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR)); | 
|  |  | 
|  | const int64_t stream_result = adapter->ProcessBytes(stream_frames); | 
|  | EXPECT_EQ(stream_frames.size(), stream_result); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ClientHandlesMetadata) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  |  | 
|  | testing::InSequence s; | 
|  |  | 
|  | const std::vector<const 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(HEADERS, stream_id1, _, 0x5)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 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({spdy::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::NO_ERROR)); | 
|  |  | 
|  | const int64_t stream_result = adapter->ProcessBytes(stream_frames); | 
|  | EXPECT_EQ(stream_frames.size(), stream_result); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ClientHandlesMetadataWithError) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  |  | 
|  | testing::InSequence s; | 
|  |  | 
|  | const std::vector<const 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(HEADERS, stream_id1, _, 0x5)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0)); | 
|  |  | 
|  | int result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | 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, _)) | 
|  | .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); | 
|  | // The false return from OnMetadataForStream() results in a connection error. | 
|  | EXPECT_EQ(stream_result, NGHTTP2_ERR_CALLBACK_FAILURE); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  | EXPECT_TRUE(adapter->want_read());  // Even after an error. Why? | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ClientHandlesInvalidTrailers) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  |  | 
|  | testing::InSequence s; | 
|  |  | 
|  | const std::vector<const 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(HEADERS, stream_id1, _, 0x5)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 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({spdy::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) | 
|  | .Data(1, "This is the response body.") | 
|  | .Headers(1, {{":bad-status", "9000"}}, | 
|  | /*fin=*/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, ":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, 26, DATA, 0)); | 
|  | EXPECT_CALL(visitor, OnBeginDataForStream(1, 26)); | 
|  | EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body.")); | 
|  | EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5)); | 
|  | EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); | 
|  | EXPECT_CALL( | 
|  | visitor, | 
|  | OnErrorDebug("Invalid HTTP header field was received: frame type: 1, " | 
|  | "stream: 1, name: [:bad-status], value: [9000]")); | 
|  | EXPECT_CALL( | 
|  | visitor, | 
|  | OnInvalidFrame(1, Http2VisitorInterface::InvalidFrameError::kHttpHeader)); | 
|  |  | 
|  | // Bad status trailer will cause a PROTOCOL_ERROR. The header is never | 
|  | // delivered in an OnHeaderForStream callback. | 
|  |  | 
|  | const int64_t stream_result = adapter->ProcessBytes(stream_frames); | 
|  | EXPECT_EQ(stream_frames.size(), stream_result); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, stream_id1, 4, 0x0)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, stream_id1, 4, 0x0, 1)); | 
|  | EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::PROTOCOL_ERROR)); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS, | 
|  | spdy::SpdyFrameType::RST_STREAM})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ClientRstStreamWhileHandlingHeaders) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  |  | 
|  | testing::InSequence s; | 
|  |  | 
|  | const std::vector<const 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(HEADERS, stream_id1, _, 0x5)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 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({spdy::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) | 
|  | .Data(1, "This is the response body.") | 
|  | .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, ":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")) | 
|  | .WillOnce(testing::DoAll( | 
|  | testing::InvokeWithoutArgs([&adapter]() { | 
|  | adapter->SubmitRst(1, Http2ErrorCode::REFUSED_STREAM); | 
|  | }), | 
|  | testing::Return(Http2VisitorInterface::HEADER_RST_STREAM))); | 
|  |  | 
|  | const int64_t stream_result = adapter->ProcessBytes(stream_frames); | 
|  | EXPECT_EQ(stream_frames.size(), stream_result); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, stream_id1, 4, 0x0)); | 
|  | EXPECT_CALL(visitor, | 
|  | OnFrameSent(RST_STREAM, stream_id1, 4, 0x0, | 
|  | static_cast<int>(Http2ErrorCode::REFUSED_STREAM))); | 
|  | EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::REFUSED_STREAM)); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS, | 
|  | spdy::SpdyFrameType::RST_STREAM})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ClientConnectionErrorWhileHandlingHeaders) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  |  | 
|  | testing::InSequence s; | 
|  |  | 
|  | const std::vector<const 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(HEADERS, stream_id1, _, 0x5)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 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({spdy::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) | 
|  | .Data(1, "This is the response body.") | 
|  | .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, ":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")) | 
|  | .WillOnce( | 
|  | testing::Return(Http2VisitorInterface::HEADER_CONNECTION_ERROR)); | 
|  | // Translation to nghttp2 treats this error as a general parsing error. | 
|  | EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kParseError)); | 
|  |  | 
|  | const int64_t stream_result = adapter->ProcessBytes(stream_frames); | 
|  | EXPECT_EQ(-902 /* NGHTTP2_ERR_CALLBACK_FAILURE */, stream_result); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ClientConnectionErrorWhileHandlingHeadersOnly) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  |  | 
|  | testing::InSequence s; | 
|  |  | 
|  | const std::vector<const 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(HEADERS, stream_id1, _, 0x5)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 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({spdy::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=*/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, 5)); | 
|  | 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")) | 
|  | .WillOnce( | 
|  | testing::Return(Http2VisitorInterface::HEADER_CONNECTION_ERROR)); | 
|  | // Translation to nghttp2 treats this error as a general parsing error. | 
|  | EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kParseError)); | 
|  |  | 
|  | const int64_t stream_result = adapter->ProcessBytes(stream_frames); | 
|  | EXPECT_EQ(-902 /* NGHTTP2_ERR_CALLBACK_FAILURE */, stream_result); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ClientRejectsHeaders) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  |  | 
|  | testing::InSequence s; | 
|  |  | 
|  | const std::vector<const 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(HEADERS, stream_id1, _, 0x5)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 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({spdy::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) | 
|  | .Data(1, "This is the response body.") | 
|  | .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)) | 
|  | .WillOnce(testing::Return(false)); | 
|  | // Rejecting headers leads to a connection error. | 
|  | EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kParseError)); | 
|  |  | 
|  | const int64_t stream_result = adapter->ProcessBytes(stream_frames); | 
|  | EXPECT_EQ(NGHTTP2_ERR_CALLBACK_FAILURE, stream_result); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ClientFailsOnGoAway) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  |  | 
|  | testing::InSequence s; | 
|  |  | 
|  | const std::vector<const 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(HEADERS, stream_id1, _, 0x5)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 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({spdy::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) | 
|  | .GoAway(1, Http2ErrorCode::INTERNAL_ERROR, "indigestion") | 
|  | .Data(1, "This is the response body.") | 
|  | .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, ":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(0, _, GOAWAY, 0)); | 
|  | EXPECT_CALL(visitor, | 
|  | OnGoAway(1, Http2ErrorCode::INTERNAL_ERROR, "indigestion")) | 
|  | .WillOnce(testing::Return(false)); | 
|  | EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kParseError)); | 
|  |  | 
|  | const int64_t stream_result = adapter->ProcessBytes(stream_frames); | 
|  | EXPECT_EQ(NGHTTP2_ERR_CALLBACK_FAILURE, stream_result); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ClientSubmitRequest) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  | int result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | // Client preface does not appear to include the mandatory SETTINGS frame. | 
|  | EXPECT_THAT(visitor.data(), | 
|  | testing::StrEq(spdy::kHttp2ConnectionHeaderPrefix)); | 
|  | visitor.Clear(); | 
|  |  | 
|  | const std::string initial_frames = | 
|  | TestFrameSequence().ServerPreface().Serialize(); | 
|  | testing::InSequence s; | 
|  |  | 
|  | // Server preface (empty SETTINGS) | 
|  | EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); | 
|  | EXPECT_CALL(visitor, OnSettingsStart()); | 
|  | EXPECT_CALL(visitor, OnSettingsEnd()); | 
|  |  | 
|  | const int64_t initial_result = adapter->ProcessBytes(initial_frames); | 
|  | EXPECT_EQ(initial_frames.size(), initial_result); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); | 
|  |  | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); | 
|  | visitor.Clear(); | 
|  |  | 
|  | EXPECT_EQ(0, adapter->GetHpackEncoderDynamicTableSize()); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  | const char* kSentinel = ""; | 
|  | const absl::string_view kBody = "This is an example request body."; | 
|  | auto body1 = absl::make_unique<TestDataFrameSource>(visitor, true); | 
|  | body1->AppendPayload(kBody); | 
|  | body1->EndData(); | 
|  | int stream_id = | 
|  | adapter->SubmitRequest(ToHeaders({{":method", "POST"}, | 
|  | {":scheme", "http"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/one"}}), | 
|  | std::move(body1), const_cast<char*>(kSentinel)); | 
|  | EXPECT_GT(stream_id, 0); | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, _, 0x1, 0)); | 
|  |  | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  |  | 
|  | EXPECT_EQ(kInitialFlowControlWindowSize, | 
|  | adapter->GetStreamReceiveWindowSize(stream_id)); | 
|  | EXPECT_EQ(kInitialFlowControlWindowSize, adapter->GetReceiveWindowSize()); | 
|  | EXPECT_EQ(kInitialFlowControlWindowSize, | 
|  | adapter->GetStreamReceiveWindowLimit(stream_id)); | 
|  |  | 
|  | EXPECT_GT(adapter->GetHpackEncoderDynamicTableSize(), 0); | 
|  |  | 
|  | // Some data was sent, so the remaining send window size should be less than | 
|  | // the default. | 
|  | EXPECT_LT(adapter->GetStreamSendWindowSize(stream_id), | 
|  | kInitialFlowControlWindowSize); | 
|  | EXPECT_GT(adapter->GetStreamSendWindowSize(stream_id), 0); | 
|  | // Send window for a nonexistent stream is not available. | 
|  | EXPECT_EQ(-1, adapter->GetStreamSendWindowSize(stream_id + 2)); | 
|  |  | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS, | 
|  | spdy::SpdyFrameType::DATA})); | 
|  | EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody)); | 
|  | visitor.Clear(); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  |  | 
|  | stream_id = | 
|  | adapter->SubmitRequest(ToHeaders({{":method", "POST"}, | 
|  | {":scheme", "http"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/one"}}), | 
|  | nullptr, nullptr); | 
|  | EXPECT_GT(stream_id, 0); | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  | const char* kSentinel2 = "arbitrary pointer 2"; | 
|  | EXPECT_EQ(nullptr, adapter->GetStreamUserData(stream_id)); | 
|  | adapter->SetStreamUserData(stream_id, const_cast<char*>(kSentinel2)); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x5)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x5, 0)); | 
|  |  | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS})); | 
|  |  | 
|  | EXPECT_EQ(kSentinel2, adapter->GetStreamUserData(stream_id)); | 
|  |  | 
|  | // No data was sent (just HEADERS), so the remaining send window size should | 
|  | // still be the default. | 
|  | EXPECT_EQ(adapter->GetStreamSendWindowSize(stream_id), | 
|  | kInitialFlowControlWindowSize); | 
|  | } | 
|  |  | 
|  | // This is really a test of the MakeZeroCopyDataFrameSource adapter, but I | 
|  | // wasn't sure where else to put it. | 
|  | TEST(NgHttp2AdapterTest, ClientSubmitRequestWithDataProvider) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  | int result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | // Client preface does not appear to include the mandatory SETTINGS frame. | 
|  | EXPECT_THAT(visitor.data(), | 
|  | testing::StrEq(spdy::kHttp2ConnectionHeaderPrefix)); | 
|  | visitor.Clear(); | 
|  |  | 
|  | const std::string initial_frames = | 
|  | TestFrameSequence().ServerPreface().Serialize(); | 
|  | testing::InSequence s; | 
|  |  | 
|  | // Server preface (empty SETTINGS) | 
|  | EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); | 
|  | EXPECT_CALL(visitor, OnSettingsStart()); | 
|  | EXPECT_CALL(visitor, OnSettingsEnd()); | 
|  |  | 
|  | const int64_t initial_result = adapter->ProcessBytes(initial_frames); | 
|  | EXPECT_EQ(initial_frames.size(), initial_result); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); | 
|  |  | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); | 
|  | visitor.Clear(); | 
|  |  | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  | const absl::string_view kBody = "This is an example request body."; | 
|  | // This test will use TestDataSource as the source of the body payload data. | 
|  | TestDataSource body1{kBody}; | 
|  | // The TestDataSource is wrapped in the nghttp2_data_provider data type. | 
|  | nghttp2_data_provider provider = body1.MakeDataProvider(); | 
|  | nghttp2_send_data_callback send_callback = &TestSendCallback; | 
|  |  | 
|  | // This call transforms it back into a DataFrameSource, which is compatible | 
|  | // with the Http2Adapter API. | 
|  | std::unique_ptr<DataFrameSource> frame_source = | 
|  | MakeZeroCopyDataFrameSource(provider, &visitor, std::move(send_callback)); | 
|  | int stream_id = | 
|  | adapter->SubmitRequest(ToHeaders({{":method", "POST"}, | 
|  | {":scheme", "http"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/one"}}), | 
|  | std::move(frame_source), nullptr); | 
|  | EXPECT_GT(stream_id, 0); | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, _, 0x1, 0)); | 
|  |  | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS, | 
|  | spdy::SpdyFrameType::DATA})); | 
|  | EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody)); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  | } | 
|  |  | 
|  | // This test verifies how nghttp2 behaves when a data source becomes | 
|  | // read-blocked. | 
|  | TEST(NgHttp2AdapterTest, ClientSubmitRequestWithDataProviderAndReadBlock) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  |  | 
|  | const absl::string_view kBody = "This is an example request body."; | 
|  | // This test will use TestDataSource as the source of the body payload data. | 
|  | TestDataSource body1{kBody}; | 
|  | body1.set_is_data_available(false); | 
|  | // The TestDataSource is wrapped in the nghttp2_data_provider data type. | 
|  | nghttp2_data_provider provider = body1.MakeDataProvider(); | 
|  | nghttp2_send_data_callback send_callback = &TestSendCallback; | 
|  |  | 
|  | // This call transforms it back into a DataFrameSource, which is compatible | 
|  | // with the Http2Adapter API. | 
|  | std::unique_ptr<DataFrameSource> frame_source = | 
|  | MakeZeroCopyDataFrameSource(provider, &visitor, std::move(send_callback)); | 
|  | int stream_id = | 
|  | adapter->SubmitRequest(ToHeaders({{":method", "POST"}, | 
|  | {":scheme", "http"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/one"}}), | 
|  | std::move(frame_source), nullptr); | 
|  | EXPECT_GT(stream_id, 0); | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0)); | 
|  |  | 
|  | int result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | // Client preface does not appear to include the mandatory SETTINGS frame. | 
|  | absl::string_view serialized = visitor.data(); | 
|  | EXPECT_THAT(serialized, | 
|  | testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); | 
|  | serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); | 
|  | EXPECT_THAT(serialized, EqualsFrames({spdy::SpdyFrameType::HEADERS})); | 
|  | visitor.Clear(); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  |  | 
|  | // Resume the deferred stream. | 
|  | body1.set_is_data_available(true); | 
|  | EXPECT_TRUE(adapter->ResumeStream(stream_id)); | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, _, 0x1, 0)); | 
|  |  | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::DATA})); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  |  | 
|  | // Stream data is done, so this stream cannot be resumed. | 
|  | EXPECT_FALSE(adapter->ResumeStream(stream_id)); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  | } | 
|  |  | 
|  | // This test verifies how nghttp2 behaves when a data source is read block, then | 
|  | // ends with an empty DATA frame. | 
|  | TEST(NgHttp2AdapterTest, ClientSubmitRequestEmptyDataWithFin) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  |  | 
|  | const absl::string_view kEmptyBody = ""; | 
|  | // This test will use TestDataSource as the source of the body payload data. | 
|  | TestDataSource body1{kEmptyBody}; | 
|  | body1.set_is_data_available(false); | 
|  | // The TestDataSource is wrapped in the nghttp2_data_provider data type. | 
|  | nghttp2_data_provider provider = body1.MakeDataProvider(); | 
|  | nghttp2_send_data_callback send_callback = &TestSendCallback; | 
|  |  | 
|  | // This call transforms it back into a DataFrameSource, which is compatible | 
|  | // with the Http2Adapter API. | 
|  | std::unique_ptr<DataFrameSource> frame_source = | 
|  | MakeZeroCopyDataFrameSource(provider, &visitor, std::move(send_callback)); | 
|  | int stream_id = | 
|  | adapter->SubmitRequest(ToHeaders({{":method", "POST"}, | 
|  | {":scheme", "http"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/one"}}), | 
|  | std::move(frame_source), nullptr); | 
|  | EXPECT_GT(stream_id, 0); | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0)); | 
|  |  | 
|  | int result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | // Client preface does not appear to include the mandatory SETTINGS frame. | 
|  | absl::string_view serialized = visitor.data(); | 
|  | EXPECT_THAT(serialized, | 
|  | testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); | 
|  | serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); | 
|  | EXPECT_THAT(serialized, EqualsFrames({spdy::SpdyFrameType::HEADERS})); | 
|  | visitor.Clear(); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  |  | 
|  | // Resume the deferred stream. | 
|  | body1.set_is_data_available(true); | 
|  | EXPECT_TRUE(adapter->ResumeStream(stream_id)); | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, 0, 0x1, 0)); | 
|  |  | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::DATA})); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  |  | 
|  | // Stream data is done, so this stream cannot be resumed. | 
|  | EXPECT_FALSE(adapter->ResumeStream(stream_id)); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  | } | 
|  |  | 
|  | // This test verifies how nghttp2 behaves when a connection becomes | 
|  | // write-blocked. | 
|  | TEST(NgHttp2AdapterTest, ClientSubmitRequestWithDataProviderAndWriteBlock) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  |  | 
|  | const absl::string_view kBody = "This is an example request body."; | 
|  | // This test will use TestDataSource as the source of the body payload data. | 
|  | TestDataSource body1{kBody}; | 
|  | // The TestDataSource is wrapped in the nghttp2_data_provider data type. | 
|  | nghttp2_data_provider provider = body1.MakeDataProvider(); | 
|  | nghttp2_send_data_callback send_callback = &TestSendCallback; | 
|  |  | 
|  | // This call transforms it back into a DataFrameSource, which is compatible | 
|  | // with the Http2Adapter API. | 
|  | std::unique_ptr<DataFrameSource> frame_source = | 
|  | MakeZeroCopyDataFrameSource(provider, &visitor, std::move(send_callback)); | 
|  | int stream_id = | 
|  | adapter->SubmitRequest(ToHeaders({{":method", "POST"}, | 
|  | {":scheme", "http"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/one"}}), | 
|  | std::move(frame_source), nullptr); | 
|  | EXPECT_GT(stream_id, 0); | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | visitor.set_is_write_blocked(true); | 
|  | int result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | EXPECT_THAT(visitor.data(), testing::IsEmpty()); | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, _, 0x1, 0)); | 
|  |  | 
|  | visitor.set_is_write_blocked(false); | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  |  | 
|  | // Client preface does not appear to include the mandatory SETTINGS frame. | 
|  | absl::string_view serialized = visitor.data(); | 
|  | EXPECT_THAT(serialized, | 
|  | testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); | 
|  | serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); | 
|  | EXPECT_THAT(serialized, EqualsFrames({spdy::SpdyFrameType::HEADERS, | 
|  | spdy::SpdyFrameType::DATA})); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, SubmitMetadata) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  |  | 
|  | auto source = absl::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(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, | 
|  | testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); | 
|  | serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); | 
|  | EXPECT_THAT( | 
|  | serialized, | 
|  | EqualsFrames({static_cast<spdy::SpdyFrameType>(kMetadataFrameType)})); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, SubmitMetadataMultipleFrames) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  |  | 
|  | const auto kLargeValue = std::string(63 * 1024, 'a'); | 
|  | auto source = absl::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(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, | 
|  | testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); | 
|  | serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); | 
|  | EXPECT_THAT( | 
|  | serialized, | 
|  | EqualsFrames({static_cast<spdy::SpdyFrameType>(kMetadataFrameType), | 
|  | static_cast<spdy::SpdyFrameType>(kMetadataFrameType), | 
|  | static_cast<spdy::SpdyFrameType>(kMetadataFrameType), | 
|  | static_cast<spdy::SpdyFrameType>(kMetadataFrameType)})); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, SubmitConnectionMetadata) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  |  | 
|  | auto source = absl::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(kMetadataFrameType, 0, _, 0x4)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 0, _, 0x4, 0)); | 
|  |  | 
|  | int result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | absl::string_view serialized = visitor.data(); | 
|  | EXPECT_THAT(serialized, | 
|  | testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); | 
|  | serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); | 
|  | EXPECT_THAT( | 
|  | serialized, | 
|  | EqualsFrames({static_cast<spdy::SpdyFrameType>(kMetadataFrameType)})); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ClientObeysMaxConcurrentStreams) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  | int result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | // Client preface does not appear to include the mandatory SETTINGS frame. | 
|  | EXPECT_THAT(visitor.data(), | 
|  | testing::StrEq(spdy::kHttp2ConnectionHeaderPrefix)); | 
|  | visitor.Clear(); | 
|  |  | 
|  | const std::string initial_frames = | 
|  | TestFrameSequence() | 
|  | .ServerPreface({{.id = MAX_CONCURRENT_STREAMS, .value = 1}}) | 
|  | .Serialize(); | 
|  | testing::InSequence s; | 
|  |  | 
|  | // Server preface (SETTINGS with MAX_CONCURRENT_STREAMS) | 
|  | EXPECT_CALL(visitor, OnFrameHeader(0, 6, SETTINGS, 0)); | 
|  | EXPECT_CALL(visitor, OnSettingsStart()); | 
|  | EXPECT_CALL(visitor, OnSetting); | 
|  | EXPECT_CALL(visitor, OnSettingsEnd()); | 
|  |  | 
|  | const int64_t initial_result = adapter->ProcessBytes(initial_frames); | 
|  | EXPECT_EQ(initial_frames.size(), initial_result); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); | 
|  |  | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); | 
|  | visitor.Clear(); | 
|  |  | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  | const absl::string_view kBody = "This is an example request body."; | 
|  | auto body1 = absl::make_unique<TestDataFrameSource>(visitor, true); | 
|  | body1->AppendPayload(kBody); | 
|  | body1->EndData(); | 
|  | const int stream_id = | 
|  | adapter->SubmitRequest(ToHeaders({{":method", "POST"}, | 
|  | {":scheme", "http"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/one"}}), | 
|  | std::move(body1), nullptr); | 
|  | EXPECT_GT(stream_id, 0); | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, _, 0x1, 0)); | 
|  |  | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  |  | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS, | 
|  | spdy::SpdyFrameType::DATA})); | 
|  | EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody)); | 
|  | visitor.Clear(); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  |  | 
|  | const int next_stream_id = | 
|  | adapter->SubmitRequest(ToHeaders({{":method", "POST"}, | 
|  | {":scheme", "http"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/two"}}), | 
|  | nullptr, nullptr); | 
|  |  | 
|  | // A new pending stream is created, but because of MAX_CONCURRENT_STREAMS, the | 
|  | // session should not want to write it at the moment. | 
|  | EXPECT_GT(next_stream_id, stream_id); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  |  | 
|  | const std::string stream_frames = | 
|  | TestFrameSequence() | 
|  | .Headers(stream_id, | 
|  | {{":status", "200"}, | 
|  | {"server", "my-fake-server"}, | 
|  | {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, | 
|  | /*fin=*/false) | 
|  | .Data(stream_id, "This is the response body.", /*fin=*/true) | 
|  | .Serialize(); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, HEADERS, 4)); | 
|  | EXPECT_CALL(visitor, OnBeginHeadersForStream(stream_id)); | 
|  | EXPECT_CALL(visitor, OnHeaderForStream(stream_id, ":status", "200")); | 
|  | EXPECT_CALL(visitor, | 
|  | OnHeaderForStream(stream_id, "server", "my-fake-server")); | 
|  | EXPECT_CALL(visitor, OnHeaderForStream(stream_id, "date", | 
|  | "Tue, 6 Apr 2021 12:54:01 GMT")); | 
|  | EXPECT_CALL(visitor, OnEndHeadersForStream(stream_id)); | 
|  | EXPECT_CALL(visitor, OnFrameHeader(stream_id, 26, DATA, 0x1)); | 
|  | EXPECT_CALL(visitor, OnBeginDataForStream(stream_id, 26)); | 
|  | EXPECT_CALL(visitor, | 
|  | OnDataForStream(stream_id, "This is the response body.")); | 
|  | EXPECT_CALL(visitor, OnEndStream(stream_id)); | 
|  | EXPECT_CALL(visitor, OnCloseStream(stream_id, Http2ErrorCode::NO_ERROR)); | 
|  |  | 
|  | // The first stream should close, which should make the session want to write | 
|  | // the next stream. | 
|  | const int64_t stream_result = adapter->ProcessBytes(stream_frames); | 
|  | EXPECT_EQ(stream_frames.size(), stream_result); | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, next_stream_id, _, 0x5)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, next_stream_id, _, 0x5, 0)); | 
|  |  | 
|  | result = adapter->Send(); | 
|  | EXPECT_EQ(0, result); | 
|  |  | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS})); | 
|  | visitor.Clear(); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ClientForbidsPushPromise) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  | adapter->SubmitSettings({{ENABLE_PUSH, 0}}); | 
|  |  | 
|  | testing::InSequence s; | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 6, 0x0)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 6, 0x0, 0)); | 
|  |  | 
|  | int write_result = adapter->Send(); | 
|  | EXPECT_EQ(0, write_result); | 
|  | absl::string_view data = visitor.data(); | 
|  | EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); | 
|  | data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); | 
|  | EXPECT_THAT(data, EqualsFrames({spdy::SpdyFrameType::SETTINGS})); | 
|  |  | 
|  | visitor.Clear(); | 
|  |  | 
|  | const std::vector<const 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(HEADERS, stream_id, _, 0x5)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x5, 0)); | 
|  | write_result = adapter->Send(); | 
|  | EXPECT_EQ(0, write_result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS})); | 
|  | visitor.Clear(); | 
|  |  | 
|  | const std::vector<const Header> push_headers = | 
|  | ToHeaders({{":method", "GET"}, | 
|  | {":scheme", "http"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/push"}}); | 
|  | const std::string frames = TestFrameSequence() | 
|  | .ServerPreface() | 
|  | .SettingsAck() | 
|  | .PushPromise(stream_id, 2, push_headers) | 
|  | .Serialize(); | 
|  |  | 
|  | // Server preface (empty SETTINGS) | 
|  | EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); | 
|  | EXPECT_CALL(visitor, OnSettingsStart()); | 
|  | EXPECT_CALL(visitor, OnSettingsEnd()); | 
|  |  | 
|  | // SETTINGS ack (to acknowledge PUSH_ENABLED=0) | 
|  | EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0x1)); | 
|  | EXPECT_CALL(visitor, OnSettingsAck); | 
|  |  | 
|  | // The PUSH_PROMISE is now treated as an invalid frame. | 
|  | EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, PUSH_PROMISE, _)); | 
|  | EXPECT_CALL(visitor, OnInvalidFrame(stream_id, _)); | 
|  |  | 
|  | const int64_t read_result = adapter->ProcessBytes(frames); | 
|  | EXPECT_EQ(frames.size(), read_result); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); | 
|  | EXPECT_CALL( | 
|  | visitor, | 
|  | OnFrameSent(GOAWAY, 0, _, 0x0, | 
|  | static_cast<int32_t>(Http2ErrorCode::PROTOCOL_ERROR))); | 
|  |  | 
|  | write_result = adapter->Send(); | 
|  | EXPECT_EQ(0, write_result); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ClientForbidsPushStream) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  | adapter->SubmitSettings({{ENABLE_PUSH, 0}}); | 
|  |  | 
|  | testing::InSequence s; | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 6, 0x0)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 6, 0x0, 0)); | 
|  |  | 
|  | int write_result = adapter->Send(); | 
|  | EXPECT_EQ(0, write_result); | 
|  | absl::string_view data = visitor.data(); | 
|  | EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); | 
|  | data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); | 
|  | EXPECT_THAT(data, EqualsFrames({spdy::SpdyFrameType::SETTINGS})); | 
|  |  | 
|  | visitor.Clear(); | 
|  |  | 
|  | const std::vector<const 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(HEADERS, stream_id, _, 0x5)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x5, 0)); | 
|  | write_result = adapter->Send(); | 
|  | EXPECT_EQ(0, write_result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS})); | 
|  | visitor.Clear(); | 
|  |  | 
|  | const std::string frames = | 
|  | TestFrameSequence() | 
|  | .ServerPreface() | 
|  | .SettingsAck() | 
|  | .Headers(2, | 
|  | {{":status", "200"}, | 
|  | {"server", "my-fake-server"}, | 
|  | {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, | 
|  | /*fin=*/true) | 
|  | .Serialize(); | 
|  |  | 
|  | // Server preface (empty SETTINGS) | 
|  | EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); | 
|  | EXPECT_CALL(visitor, OnSettingsStart()); | 
|  | EXPECT_CALL(visitor, OnSettingsEnd()); | 
|  |  | 
|  | // SETTINGS ack (to acknowledge PUSH_ENABLED=0) | 
|  | EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0x1)); | 
|  | EXPECT_CALL(visitor, OnSettingsAck); | 
|  |  | 
|  | // The push HEADERS are invalid. | 
|  | EXPECT_CALL(visitor, OnFrameHeader(2, _, HEADERS, _)); | 
|  | EXPECT_CALL(visitor, OnInvalidFrame(2, _)); | 
|  |  | 
|  | const int64_t read_result = adapter->ProcessBytes(frames); | 
|  | EXPECT_EQ(frames.size(), read_result); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); | 
|  | EXPECT_CALL( | 
|  | visitor, | 
|  | OnFrameSent(GOAWAY, 0, _, 0x0, | 
|  | static_cast<int32_t>(Http2ErrorCode::PROTOCOL_ERROR))); | 
|  |  | 
|  | write_result = adapter->Send(); | 
|  | EXPECT_EQ(0, write_result); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, FailureSendingConnectionPreface) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); | 
|  |  | 
|  | visitor.set_has_write_error(); | 
|  | EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kSendError)); | 
|  |  | 
|  | int result = adapter->Send(); | 
|  | EXPECT_EQ(result, NGHTTP2_ERR_CALLBACK_FAILURE); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ServerConstruction) { | 
|  | testing::StrictMock<MockHttp2Visitor> visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
|  | ASSERT_NE(nullptr, adapter); | 
|  | EXPECT_TRUE(adapter->want_read()); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  | EXPECT_TRUE(adapter->IsServerSession()); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ServerHandlesFrames) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
|  |  | 
|  | EXPECT_EQ(0, adapter->GetHighestReceivedStreamId()); | 
|  | EXPECT_EQ(0, adapter->GetHpackDecoderDynamicTableSize()); | 
|  |  | 
|  | const std::string frames = TestFrameSequence() | 
|  | .ClientPreface() | 
|  | .Ping(42) | 
|  | .WindowUpdate(0, 1000) | 
|  | .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.") | 
|  | .Headers(3, | 
|  | {{":method", "GET"}, | 
|  | {":scheme", "http"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/two"}}, | 
|  | /*fin=*/true) | 
|  | .RstStream(3, Http2ErrorCode::CANCEL) | 
|  | .Ping(47) | 
|  | .Serialize(); | 
|  | testing::InSequence s; | 
|  |  | 
|  | const char* kSentinel1 = "arbitrary pointer 1"; | 
|  |  | 
|  | // Client preface (empty SETTINGS) | 
|  | EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); | 
|  | EXPECT_CALL(visitor, OnSettingsStart()); | 
|  | EXPECT_CALL(visitor, OnSettingsEnd()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnFrameHeader(0, 8, PING, 0)); | 
|  | EXPECT_CALL(visitor, OnPing(42, false)); | 
|  | EXPECT_CALL(visitor, OnFrameHeader(0, 4, WINDOW_UPDATE, 0)); | 
|  | EXPECT_CALL(visitor, OnWindowUpdate(0, 1000)); | 
|  | 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)) | 
|  | .WillOnce(testing::InvokeWithoutArgs([&adapter, kSentinel1]() { | 
|  | adapter->SetStreamUserData(1, const_cast<char*>(kSentinel1)); | 
|  | return true; | 
|  | })); | 
|  | EXPECT_CALL(visitor, OnFrameHeader(1, 4, WINDOW_UPDATE, 0)); | 
|  | EXPECT_CALL(visitor, OnWindowUpdate(1, 2000)); | 
|  | EXPECT_CALL(visitor, OnFrameHeader(1, 25, DATA, 0)); | 
|  | EXPECT_CALL(visitor, OnBeginDataForStream(1, 25)); | 
|  | EXPECT_CALL(visitor, OnDataForStream(1, "This is the request body.")); | 
|  | EXPECT_CALL(visitor, OnFrameHeader(3, _, HEADERS, 5)); | 
|  | EXPECT_CALL(visitor, OnBeginHeadersForStream(3)); | 
|  | EXPECT_CALL(visitor, OnHeaderForStream(3, ":method", "GET")); | 
|  | EXPECT_CALL(visitor, OnHeaderForStream(3, ":scheme", "http")); | 
|  | EXPECT_CALL(visitor, OnHeaderForStream(3, ":authority", "example.com")); | 
|  | EXPECT_CALL(visitor, OnHeaderForStream(3, ":path", "/this/is/request/two")); | 
|  | EXPECT_CALL(visitor, OnEndHeadersForStream(3)); | 
|  | EXPECT_CALL(visitor, OnEndStream(3)); | 
|  | EXPECT_CALL(visitor, OnFrameHeader(3, 4, RST_STREAM, 0)); | 
|  | EXPECT_CALL(visitor, OnRstStream(3, Http2ErrorCode::CANCEL)); | 
|  | EXPECT_CALL(visitor, OnCloseStream(3, Http2ErrorCode::CANCEL)); | 
|  | EXPECT_CALL(visitor, OnFrameHeader(0, 8, PING, 0)); | 
|  | EXPECT_CALL(visitor, OnPing(47, false)); | 
|  |  | 
|  | const int64_t result = adapter->ProcessBytes(frames); | 
|  | EXPECT_EQ(frames.size(), result); | 
|  |  | 
|  | EXPECT_EQ(kSentinel1, adapter->GetStreamUserData(1)); | 
|  |  | 
|  | EXPECT_GT(kInitialFlowControlWindowSize, | 
|  | adapter->GetStreamReceiveWindowSize(1)); | 
|  | EXPECT_EQ(adapter->GetStreamReceiveWindowSize(1), | 
|  | adapter->GetReceiveWindowSize()); | 
|  | // Upper bound should still be the original value. | 
|  | EXPECT_EQ(kInitialFlowControlWindowSize, | 
|  | adapter->GetStreamReceiveWindowLimit(1)); | 
|  |  | 
|  | EXPECT_GT(adapter->GetHpackDecoderDynamicTableSize(), 0); | 
|  |  | 
|  | // Because stream 3 has already been closed, it's not possible to set user | 
|  | // data. | 
|  | const char* kSentinel3 = "another arbitrary pointer"; | 
|  | adapter->SetStreamUserData(3, const_cast<char*>(kSentinel3)); | 
|  | EXPECT_EQ(nullptr, adapter->GetStreamUserData(3)); | 
|  |  | 
|  | EXPECT_EQ(3, adapter->GetHighestReceivedStreamId()); | 
|  |  | 
|  | EXPECT_EQ(adapter->GetSendWindowSize(), kInitialFlowControlWindowSize + 1000); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(PING, 0, 8, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(PING, 0, 8, 0x1, 0)); | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(PING, 0, 8, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(PING, 0, 8, 0x1, 0)); | 
|  |  | 
|  | int send_result = adapter->Send(); | 
|  | // Some bytes should have been serialized. | 
|  | EXPECT_EQ(0, send_result); | 
|  | // SETTINGS ack, two PING acks. | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS, | 
|  | spdy::SpdyFrameType::PING, | 
|  | spdy::SpdyFrameType::PING})); | 
|  | } | 
|  |  | 
|  | // Tests the case where the response body is in the progress of being sent while | 
|  | // trailers are queued. | 
|  | TEST(NgHttp2AdapterTest, ServerSubmitsTrailersWhileDataDeferred) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
|  |  | 
|  | 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()); | 
|  |  | 
|  | 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)); | 
|  |  | 
|  | const int64_t result = adapter->ProcessBytes(frames); | 
|  | EXPECT_EQ(frames.size(), static_cast<size_t>(result)); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | 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})); | 
|  | visitor.Clear(); | 
|  |  | 
|  | 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()); | 
|  |  | 
|  | 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()); | 
|  |  | 
|  | int trailer_result = | 
|  | adapter->SubmitTrailer(1, ToHeaders({{"final-status", "a-ok"}})); | 
|  | ASSERT_EQ(trailer_result, 0); | 
|  |  | 
|  | // Even though the data source has not finished sending data, nghttp2 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})); | 
|  | visitor.Clear(); | 
|  |  | 
|  | // Resuming the stream results in the library wanting to write again. | 
|  | body1_ptr->AppendPayload(kBody); | 
|  | body1_ptr->EndData(); | 
|  | adapter->ResumeStream(1); | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | send_result = adapter->Send(); | 
|  | EXPECT_EQ(0, send_result); | 
|  |  | 
|  | // But no data is written for the stream. | 
|  | EXPECT_THAT(visitor.data(), testing::IsEmpty()); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ServerErrorWhileHandlingHeaders) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
|  |  | 
|  | const std::string frames = TestFrameSequence() | 
|  | .ClientPreface() | 
|  | .Headers(1, | 
|  | {{":method", "POST"}, | 
|  | {":scheme", "https"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/one"}, | 
|  | {"accept", "some bogus value!"}}, | 
|  | /*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()); | 
|  |  | 
|  | 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, "accept", "some bogus value!")) | 
|  | .WillOnce(testing::Return(Http2VisitorInterface::HEADER_RST_STREAM)); | 
|  | EXPECT_CALL(visitor, OnFrameHeader(1, 4, WINDOW_UPDATE, 0)); | 
|  | EXPECT_CALL(visitor, OnWindowUpdate(1, 2000)); | 
|  | // DATA frame is not delivered to the visitor. | 
|  | 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(), result); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, 4, 0x0)); | 
|  | EXPECT_CALL(visitor, | 
|  | OnFrameSent(RST_STREAM, 1, 4, 0x0, | 
|  | static_cast<int>(Http2ErrorCode::INTERNAL_ERROR))); | 
|  | EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::INTERNAL_ERROR)); | 
|  |  | 
|  | 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::RST_STREAM})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ServerConnectionErrorWhileHandlingHeaders) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
|  |  | 
|  | const std::string frames = TestFrameSequence() | 
|  | .ClientPreface() | 
|  | .Headers(1, | 
|  | {{":method", "POST"}, | 
|  | {":scheme", "https"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/one"}, | 
|  | {"Accept", "uppercase, oh boy!"}}, | 
|  | /*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()); | 
|  |  | 
|  | 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, OnErrorDebug); | 
|  | EXPECT_CALL( | 
|  | visitor, | 
|  | OnInvalidFrame(1, Http2VisitorInterface::InvalidFrameError::kHttpHeader)) | 
|  | .WillOnce(testing::Return(false)); | 
|  | // Translation to nghttp2 treats this error as a general parsing error. | 
|  | EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kParseError)); | 
|  |  | 
|  | const int64_t result = adapter->ProcessBytes(frames); | 
|  | EXPECT_EQ(result, NGHTTP2_ERR_CALLBACK_FAILURE); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, 4, 0x0)); | 
|  | EXPECT_CALL(visitor, | 
|  | OnFrameSent(RST_STREAM, 1, 4, 0x0, | 
|  | static_cast<int>(Http2ErrorCode::PROTOCOL_ERROR))); | 
|  | EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::PROTOCOL_ERROR)); | 
|  |  | 
|  | int send_result = adapter->Send(); | 
|  | // Some bytes should have been serialized. | 
|  | EXPECT_EQ(0, send_result); | 
|  | // SETTINGS ack and RST_STREAM | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS, | 
|  | spdy::SpdyFrameType::RST_STREAM})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ServerErrorAfterHandlingHeaders) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
|  |  | 
|  | 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()); | 
|  |  | 
|  | 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)) | 
|  | .WillOnce(testing::Return(false)); | 
|  | EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kParseError)); | 
|  |  | 
|  | const int64_t result = adapter->ProcessBytes(frames); | 
|  | EXPECT_EQ(-902, result); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | 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})); | 
|  | } | 
|  |  | 
|  | // Exercises the case when a visitor chooses to reject a frame based solely on | 
|  | // the frame header, which is a fatal error for the connection. | 
|  | TEST(NgHttp2AdapterTest, ServerRejectsFrameHeader) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
|  |  | 
|  | const std::string frames = TestFrameSequence() | 
|  | .ClientPreface() | 
|  | .Ping(64) | 
|  | .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()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnFrameHeader(0, 8, PING, 0)) | 
|  | .WillOnce(testing::Return(false)); | 
|  | EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kParseError)); | 
|  |  | 
|  | const int64_t result = adapter->ProcessBytes(frames); | 
|  | EXPECT_EQ(-902, result); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | 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})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ServerRejectsBeginningOfData) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
|  |  | 
|  | const std::string frames = TestFrameSequence() | 
|  | .ClientPreface() | 
|  | .Headers(1, | 
|  | {{":method", "POST"}, | 
|  | {":scheme", "https"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/one"}}, | 
|  | /*fin=*/false) | 
|  | .Data(1, "This is the request body.") | 
|  | .Headers(3, | 
|  | {{":method", "GET"}, | 
|  | {":scheme", "http"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/two"}}, | 
|  | /*fin=*/true) | 
|  | .RstStream(3, Http2ErrorCode::CANCEL) | 
|  | .Ping(47) | 
|  | .Serialize(); | 
|  | testing::InSequence s; | 
|  |  | 
|  | // 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, 25, DATA, 0)); | 
|  | EXPECT_CALL(visitor, OnBeginDataForStream(1, 25)) | 
|  | .WillOnce(testing::Return(false)); | 
|  | EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kParseError)); | 
|  |  | 
|  | const int64_t result = adapter->ProcessBytes(frames); | 
|  | EXPECT_EQ(NGHTTP2_ERR_CALLBACK_FAILURE, result); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | 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})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ServerRejectsStreamData) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
|  |  | 
|  | const std::string frames = TestFrameSequence() | 
|  | .ClientPreface() | 
|  | .Headers(1, | 
|  | {{":method", "POST"}, | 
|  | {":scheme", "https"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/one"}}, | 
|  | /*fin=*/false) | 
|  | .Data(1, "This is the request body.") | 
|  | .Headers(3, | 
|  | {{":method", "GET"}, | 
|  | {":scheme", "http"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/two"}}, | 
|  | /*fin=*/true) | 
|  | .RstStream(3, Http2ErrorCode::CANCEL) | 
|  | .Ping(47) | 
|  | .Serialize(); | 
|  | testing::InSequence s; | 
|  |  | 
|  | // 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, 25, DATA, 0)); | 
|  | EXPECT_CALL(visitor, OnBeginDataForStream(1, 25)); | 
|  | EXPECT_CALL(visitor, OnDataForStream(1, _)).WillOnce(testing::Return(false)); | 
|  | EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kParseError)); | 
|  |  | 
|  | const int64_t result = adapter->ProcessBytes(frames); | 
|  | EXPECT_EQ(NGHTTP2_ERR_CALLBACK_FAILURE, result); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | 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})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ServerSubmitResponse) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  |  | 
|  | const std::string frames = TestFrameSequence() | 
|  | .ClientPreface() | 
|  | .Headers(1, | 
|  | {{":method", "GET"}, | 
|  | {":scheme", "https"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/one"}}, | 
|  | /*fin=*/true) | 
|  | .Serialize(); | 
|  | testing::InSequence s; | 
|  |  | 
|  | const char* kSentinel1 = "arbitrary pointer 1"; | 
|  |  | 
|  | // 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, ":method", "GET")); | 
|  | 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)) | 
|  | .WillOnce(testing::InvokeWithoutArgs([&adapter, kSentinel1]() { | 
|  | adapter->SetStreamUserData(1, const_cast<char*>(kSentinel1)); | 
|  | return true; | 
|  | })); | 
|  | EXPECT_CALL(visitor, OnEndStream(1)); | 
|  |  | 
|  | const int64_t result = adapter->ProcessBytes(frames); | 
|  | EXPECT_EQ(frames.size(), result); | 
|  |  | 
|  | EXPECT_EQ(1, adapter->GetHighestReceivedStreamId()); | 
|  |  | 
|  | // Server will want to send a SETTINGS ack. | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); | 
|  |  | 
|  | int send_result = adapter->Send(); | 
|  | EXPECT_EQ(0, send_result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); | 
|  | visitor.Clear(); | 
|  |  | 
|  | EXPECT_EQ(0, adapter->GetHpackEncoderDynamicTableSize()); | 
|  |  | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  | const absl::string_view kBody = "This is an example response body."; | 
|  | // A data fin is not sent so that the stream remains open, and the flow | 
|  | // control state can be verified. | 
|  | auto body1 = absl::make_unique<TestDataFrameSource>(visitor, false); | 
|  | body1->AppendPayload(kBody); | 
|  | int submit_result = adapter->SubmitResponse( | 
|  | 1, | 
|  | ToHeaders({{":status", "404"}, | 
|  | {"x-comment", "I have no idea what you're talking about."}}), | 
|  | std::move(body1)); | 
|  | EXPECT_EQ(submit_result, 0); | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | // Stream user data should have been set successfully after receiving headers. | 
|  | EXPECT_EQ(kSentinel1, adapter->GetStreamUserData(1)); | 
|  | adapter->SetStreamUserData(1, nullptr); | 
|  | EXPECT_EQ(nullptr, adapter->GetStreamUserData(1)); | 
|  |  | 
|  | 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(); | 
|  | EXPECT_EQ(0, send_result); | 
|  |  | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS, | 
|  | spdy::SpdyFrameType::DATA})); | 
|  | EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody)); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  |  | 
|  | // Some data was sent, so the remaining send window size should be less than | 
|  | // the default. | 
|  | EXPECT_LT(adapter->GetStreamSendWindowSize(1), kInitialFlowControlWindowSize); | 
|  | EXPECT_GT(adapter->GetStreamSendWindowSize(1), 0); | 
|  | // Send window for a nonexistent stream is not available. | 
|  | EXPECT_EQ(adapter->GetStreamSendWindowSize(3), -1); | 
|  |  | 
|  | EXPECT_GT(adapter->GetHpackEncoderDynamicTableSize(), 0); | 
|  | } | 
|  |  | 
|  | // Should also test: client attempts shutdown, server attempts shutdown after an | 
|  | // explicit GOAWAY. | 
|  | TEST(NgHttp2AdapterTest, ServerSendsShutdown) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
|  |  | 
|  | const std::string frames = TestFrameSequence() | 
|  | .ClientPreface() | 
|  | .Headers(1, | 
|  | {{":method", "POST"}, | 
|  | {":scheme", "https"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/one"}}, | 
|  | /*fin=*/false) | 
|  | .Serialize(); | 
|  | testing::InSequence s; | 
|  |  | 
|  | // 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)); | 
|  |  | 
|  | const int64_t result = adapter->ProcessBytes(frames); | 
|  | EXPECT_EQ(frames.size(), result); | 
|  |  | 
|  | adapter->SubmitShutdownNotice(); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(GOAWAY, 0, _, 0x0, 0)); | 
|  |  | 
|  | int send_result = adapter->Send(); | 
|  | EXPECT_EQ(0, send_result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS, | 
|  | spdy::SpdyFrameType::GOAWAY})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ServerSendsTrailers) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  |  | 
|  | const std::string frames = TestFrameSequence() | 
|  | .ClientPreface() | 
|  | .Headers(1, | 
|  | {{":method", "GET"}, | 
|  | {":scheme", "https"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/one"}}, | 
|  | /*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, ":method", "GET")); | 
|  | 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, OnEndStream(1)); | 
|  |  | 
|  | const int64_t result = adapter->ProcessBytes(frames); | 
|  | EXPECT_EQ(frames.size(), result); | 
|  |  | 
|  | // Server will want to send a SETTINGS ack. | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); | 
|  |  | 
|  | int send_result = adapter->Send(); | 
|  | EXPECT_EQ(0, send_result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); | 
|  | visitor.Clear(); | 
|  |  | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  | 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); | 
|  | 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(HEADERS, 1, _, 0x4)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x4, 0)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(DATA, 1, _, 0x0, 0)); | 
|  |  | 
|  | send_result = adapter->Send(); | 
|  | EXPECT_EQ(0, send_result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS, | 
|  | spdy::SpdyFrameType::DATA})); | 
|  | EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody)); | 
|  | visitor.Clear(); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  |  | 
|  | // The body source has been exhausted by the call to Send() above. | 
|  | int trailer_result = adapter->SubmitTrailer( | 
|  | 1, ToHeaders({{"final-status", "a-ok"}, | 
|  | {"x-comment", "trailers sure are cool"}})); | 
|  | ASSERT_EQ(trailer_result, 0); | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x5)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x5, 0)); | 
|  | EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR)); | 
|  |  | 
|  | send_result = adapter->Send(); | 
|  | EXPECT_EQ(0, send_result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ClientSendsContinuation) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  |  | 
|  | const std::string frames = TestFrameSequence() | 
|  | .ClientPreface() | 
|  | .Headers(1, | 
|  | {{":method", "GET"}, | 
|  | {":scheme", "https"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/one"}}, | 
|  | /*fin=*/true, | 
|  | /*add_continuation=*/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, 1)); | 
|  | 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)); | 
|  | EXPECT_CALL(visitor, OnEndStream(1)); | 
|  |  | 
|  | const int64_t result = adapter->ProcessBytes(frames); | 
|  | EXPECT_EQ(frames.size(), result); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ClientSendsMetadataWithContinuation) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
|  | 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(), result); | 
|  | EXPECT_EQ(TestFrameSequence::MetadataBlockForPayload( | 
|  | "Example connection metadata in multiple frames"), | 
|  | absl::StrJoin(visitor.GetMetadata(0), "")); | 
|  | EXPECT_EQ(TestFrameSequence::MetadataBlockForPayload( | 
|  | "Some stream metadata that's also sent in multiple frames"), | 
|  | absl::StrJoin(visitor.GetMetadata(1), "")); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ServerSubmitsResponseWithDataSourceError) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  |  | 
|  | const std::string frames = TestFrameSequence() | 
|  | .ClientPreface() | 
|  | .Headers(1, | 
|  | {{":method", "GET"}, | 
|  | {":scheme", "https"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/one"}}, | 
|  | /*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, ":method", "GET")); | 
|  | 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, OnEndStream(1)); | 
|  |  | 
|  | const int64_t result = adapter->ProcessBytes(frames); | 
|  | EXPECT_EQ(frames.size(), static_cast<size_t>(result)); | 
|  |  | 
|  | auto body1 = absl::make_unique<TestDataFrameSource>(visitor, false); | 
|  | body1->SimulateError(); | 
|  | 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, _, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x4)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x4, 0)); | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, _, 0x0)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 1, _, 0x0, 2)); | 
|  | EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::INTERNAL_ERROR)); | 
|  |  | 
|  | int send_result = adapter->Send(); | 
|  | EXPECT_EQ(0, send_result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS, | 
|  | spdy::SpdyFrameType::HEADERS, | 
|  | spdy::SpdyFrameType::RST_STREAM})); | 
|  | visitor.Clear(); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  |  | 
|  | int trailer_result = | 
|  | adapter->SubmitTrailer(1, ToHeaders({{":final-status", "a-ok"}})); | 
|  | // The library does not object to the user queuing trailers, even through the | 
|  | // stream has already been closed. | 
|  | EXPECT_EQ(trailer_result, 0); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ServerSendsInvalidTrailers) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  |  | 
|  | const std::string frames = TestFrameSequence() | 
|  | .ClientPreface() | 
|  | .Headers(1, | 
|  | {{":method", "GET"}, | 
|  | {":scheme", "https"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/one"}}, | 
|  | /*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, ":method", "GET")); | 
|  | 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, OnEndStream(1)); | 
|  |  | 
|  | const int64_t result = adapter->ProcessBytes(frames); | 
|  | EXPECT_EQ(frames.size(), 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 = absl::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, _, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 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({spdy::SpdyFrameType::SETTINGS, | 
|  | spdy::SpdyFrameType::HEADERS, | 
|  | spdy::SpdyFrameType::DATA})); | 
|  | EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody)); | 
|  | visitor.Clear(); | 
|  | EXPECT_FALSE(adapter->want_write()); | 
|  |  | 
|  | // The body source has been exhausted by the call to Send() above. | 
|  | 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(HEADERS, 1, _, 0x5)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x5, 0)); | 
|  | EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR)); | 
|  |  | 
|  | send_result = adapter->Send(); | 
|  | EXPECT_EQ(0, send_result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ServerDropsNewStreamBelowWatermark) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
|  |  | 
|  | EXPECT_EQ(0, adapter->GetHighestReceivedStreamId()); | 
|  |  | 
|  | const std::string frames = TestFrameSequence() | 
|  | .ClientPreface() | 
|  | .Headers(3, | 
|  | {{":method", "POST"}, | 
|  | {":scheme", "https"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/one"}}, | 
|  | /*fin=*/false) | 
|  | .Data(3, "This is the request body.") | 
|  | .Headers(1, | 
|  | {{":method", "GET"}, | 
|  | {":scheme", "http"}, | 
|  | {":authority", "example.com"}, | 
|  | {":path", "/this/is/request/two"}}, | 
|  | /*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()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnFrameHeader(3, _, HEADERS, 4)); | 
|  | EXPECT_CALL(visitor, OnBeginHeadersForStream(3)); | 
|  | EXPECT_CALL(visitor, OnHeaderForStream(3, ":method", "POST")); | 
|  | EXPECT_CALL(visitor, OnHeaderForStream(3, ":scheme", "https")); | 
|  | EXPECT_CALL(visitor, OnHeaderForStream(3, ":authority", "example.com")); | 
|  | EXPECT_CALL(visitor, OnHeaderForStream(3, ":path", "/this/is/request/one")); | 
|  | EXPECT_CALL(visitor, OnEndHeadersForStream(3)); | 
|  | EXPECT_CALL(visitor, OnFrameHeader(3, 25, DATA, 0)); | 
|  | EXPECT_CALL(visitor, OnBeginDataForStream(3, 25)); | 
|  | EXPECT_CALL(visitor, OnDataForStream(3, "This is the request body.")); | 
|  |  | 
|  | // It looks like nghttp2 delivers the under-watermark frame header but | 
|  | // otherwise silently drops the rest of the frame without error. | 
|  | EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5)); | 
|  | EXPECT_CALL(visitor, OnInvalidFrame).Times(0); | 
|  | EXPECT_CALL(visitor, OnConnectionError).Times(0); | 
|  |  | 
|  | const int64_t result = adapter->ProcessBytes(frames); | 
|  | EXPECT_EQ(frames.size(), result); | 
|  |  | 
|  | EXPECT_EQ(3, adapter->GetHighestReceivedStreamId()); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | 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})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ServerForbidsWindowUpdateOnIdleStream) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
|  |  | 
|  | EXPECT_EQ(0, adapter->GetHighestReceivedStreamId()); | 
|  |  | 
|  | const std::string frames = | 
|  | TestFrameSequence().ClientPreface().WindowUpdate(1, 42).Serialize(); | 
|  |  | 
|  | testing::InSequence s; | 
|  |  | 
|  | // 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, 4, WINDOW_UPDATE, 0)); | 
|  | EXPECT_CALL(visitor, OnInvalidFrame(1, _)); | 
|  |  | 
|  | const int64_t result = adapter->ProcessBytes(frames); | 
|  | EXPECT_EQ(frames.size(), result); | 
|  |  | 
|  | EXPECT_EQ(0, adapter->GetHighestReceivedStreamId()); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); | 
|  | EXPECT_CALL(visitor, | 
|  | OnFrameSent(GOAWAY, 0, _, 0x0, | 
|  | static_cast<int>(Http2ErrorCode::PROTOCOL_ERROR))); | 
|  |  | 
|  | int send_result = adapter->Send(); | 
|  | // Some bytes should have been serialized. | 
|  | EXPECT_EQ(0, send_result); | 
|  | // The GOAWAY apparently causes the SETTINGS ack to be dropped. | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::GOAWAY})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ServerForbidsDataOnIdleStream) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
|  |  | 
|  | EXPECT_EQ(0, adapter->GetHighestReceivedStreamId()); | 
|  |  | 
|  | const std::string frames = TestFrameSequence() | 
|  | .ClientPreface() | 
|  | .Data(1, "Sorry, out of order") | 
|  | .Serialize(); | 
|  |  | 
|  | testing::InSequence s; | 
|  |  | 
|  | // Client preface (empty SETTINGS) | 
|  | EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); | 
|  | EXPECT_CALL(visitor, OnSettingsStart()); | 
|  | EXPECT_CALL(visitor, OnSettingsEnd()); | 
|  |  | 
|  | const int64_t result = adapter->ProcessBytes(frames); | 
|  | EXPECT_EQ(frames.size(), result); | 
|  |  | 
|  | EXPECT_EQ(0, adapter->GetHighestReceivedStreamId()); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | // In this case, nghttp2 goes straight to GOAWAY and does not invoke the | 
|  | // invalid frame callback. | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); | 
|  | EXPECT_CALL(visitor, | 
|  | OnFrameSent(GOAWAY, 0, _, 0x0, | 
|  | static_cast<int>(Http2ErrorCode::PROTOCOL_ERROR))); | 
|  |  | 
|  | int send_result = adapter->Send(); | 
|  | // Some bytes should have been serialized. | 
|  | EXPECT_EQ(0, send_result); | 
|  | // The GOAWAY apparently causes the SETTINGS ack to be dropped. | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::GOAWAY})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, ServerForbidsRstStreamOnIdleStream) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
|  |  | 
|  | EXPECT_EQ(0, adapter->GetHighestReceivedStreamId()); | 
|  |  | 
|  | const std::string frames = | 
|  | TestFrameSequence() | 
|  | .ClientPreface() | 
|  | .RstStream(1, Http2ErrorCode::ENHANCE_YOUR_CALM) | 
|  | .Serialize(); | 
|  |  | 
|  | testing::InSequence s; | 
|  |  | 
|  | // 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, _, RST_STREAM, 0)); | 
|  | EXPECT_CALL(visitor, OnInvalidFrame(1, _)); | 
|  |  | 
|  | const int64_t result = adapter->ProcessBytes(frames); | 
|  | EXPECT_EQ(frames.size(), result); | 
|  |  | 
|  | EXPECT_EQ(0, adapter->GetHighestReceivedStreamId()); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); | 
|  | EXPECT_CALL(visitor, | 
|  | OnFrameSent(GOAWAY, 0, _, 0x0, | 
|  | static_cast<int>(Http2ErrorCode::PROTOCOL_ERROR))); | 
|  |  | 
|  | int send_result = adapter->Send(); | 
|  | // Some bytes should have been serialized. | 
|  | EXPECT_EQ(0, send_result); | 
|  | // The GOAWAY apparently causes the SETTINGS ack to be dropped. | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::GOAWAY})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, AutomaticSettingsAndPingAcks) { | 
|  | DataSavingVisitor visitor; | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); | 
|  | const std::string frames = | 
|  | TestFrameSequence().ClientPreface().Ping(42).Serialize(); | 
|  | testing::InSequence s; | 
|  |  | 
|  | // Client preface (empty SETTINGS) | 
|  | EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); | 
|  | EXPECT_CALL(visitor, OnSettingsStart()); | 
|  | EXPECT_CALL(visitor, OnSettingsEnd()); | 
|  | // PING | 
|  | EXPECT_CALL(visitor, OnFrameHeader(0, _, PING, 0)); | 
|  | EXPECT_CALL(visitor, OnPing(42, false)); | 
|  |  | 
|  | const int64_t read_result = adapter->ProcessBytes(frames); | 
|  | EXPECT_EQ(static_cast<size_t>(read_result), frames.size()); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | // Server preface does not appear to include the mandatory SETTINGS frame. | 
|  | // SETTINGS ack | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); | 
|  | // PING ack | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(PING, 0, _, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(PING, 0, _, 0x1, 0)); | 
|  |  | 
|  | int send_result = adapter->Send(); | 
|  | EXPECT_EQ(0, send_result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS, | 
|  | spdy::SpdyFrameType::PING})); | 
|  | } | 
|  |  | 
|  | TEST(NgHttp2AdapterTest, AutomaticPingAcksDisabled) { | 
|  | DataSavingVisitor visitor; | 
|  | nghttp2_option* options; | 
|  | nghttp2_option_new(&options); | 
|  | nghttp2_option_set_no_auto_ping_ack(options, 1); | 
|  | auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor, options); | 
|  | nghttp2_option_del(options); | 
|  |  | 
|  | const std::string frames = | 
|  | TestFrameSequence().ClientPreface().Ping(42).Serialize(); | 
|  | testing::InSequence s; | 
|  |  | 
|  | // Client preface (empty SETTINGS) | 
|  | EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); | 
|  | EXPECT_CALL(visitor, OnSettingsStart()); | 
|  | EXPECT_CALL(visitor, OnSettingsEnd()); | 
|  | // PING | 
|  | EXPECT_CALL(visitor, OnFrameHeader(0, _, PING, 0)); | 
|  | EXPECT_CALL(visitor, OnPing(42, false)); | 
|  |  | 
|  | const int64_t read_result = adapter->ProcessBytes(frames); | 
|  | EXPECT_EQ(static_cast<size_t>(read_result), frames.size()); | 
|  |  | 
|  | EXPECT_TRUE(adapter->want_write()); | 
|  |  | 
|  | // Server preface does not appear to include the mandatory SETTINGS frame. | 
|  | // SETTINGS ack | 
|  | EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); | 
|  | EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); | 
|  | // No PING ack expected because automatic PING acks are disabled. | 
|  |  | 
|  | int send_result = adapter->Send(); | 
|  | EXPECT_EQ(0, send_result); | 
|  | EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace test | 
|  | }  // namespace adapter | 
|  | }  // namespace http2 |