#include "http2/adapter/oghttp2_adapter.h" #include #include "absl/strings/str_join.h" #include "http2/adapter/http2_protocol.h" #include "http2/adapter/http2_visitor_interface.h" #include "http2/adapter/mock_http2_visitor.h" #include "http2/adapter/oghttp2_util.h" #include "http2/adapter/test_frame_sequence.h" #include "http2/adapter/test_utils.h" #include "common/platform/api/quiche_test.h" #include "common/platform/api/quiche_test_helpers.h" namespace http2 { namespace adapter { namespace test { namespace { using ConnectionError = Http2VisitorInterface::ConnectionError; using spdy::SpdyFrameType; using testing::_; enum FrameType { DATA, HEADERS, PRIORITY, RST_STREAM, SETTINGS, PUSH_PROMISE, PING, GOAWAY, WINDOW_UPDATE, CONTINUATION, }; // TODO(birenroy): replace numeric flag values with named constants enum FrameFlag { END_STREAM = 0x01, ACK = END_STREAM, END_HEADERS = 0x04, }; TEST(OgHttp2AdapterTest, IsServerSession) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); EXPECT_TRUE(adapter->IsServerSession()); } TEST(OgHttp2AdapterTest, ProcessBytes) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence seq; EXPECT_CALL(visitor, OnFrameHeader(0, 0, 4, 0)); EXPECT_CALL(visitor, OnSettingsStart()); EXPECT_CALL(visitor, OnSettingsEnd()); EXPECT_CALL(visitor, OnFrameHeader(0, 8, 6, 0)); EXPECT_CALL(visitor, OnPing(17, false)); adapter->ProcessBytes( TestFrameSequence().ClientPreface().Ping(17).Serialize()); } TEST(OgHttp2AdapterTest, InitialSettingsNoExtendedConnect) { DataSavingVisitor client_visitor; OgHttp2Adapter::Options client_options{.perspective = Perspective::kClient, .max_header_list_bytes = 42, .allow_extended_connect = false}; auto client_adapter = OgHttp2Adapter::Create(client_visitor, client_options); DataSavingVisitor server_visitor; OgHttp2Adapter::Options server_options{.perspective = Perspective::kServer, .allow_extended_connect = false}; auto server_adapter = OgHttp2Adapter::Create(server_visitor, server_options); testing::InSequence s; // Client sends the connection preface, including the initial SETTINGS. EXPECT_CALL(client_visitor, OnBeforeFrameSent(SETTINGS, 0, 12, 0x0)); EXPECT_CALL(client_visitor, OnFrameSent(SETTINGS, 0, 12, 0x0, 0)); { int result = client_adapter->Send(); EXPECT_EQ(0, result); absl::string_view data = client_visitor.data(); EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); EXPECT_THAT(data, EqualsFrames({SpdyFrameType::SETTINGS})); } // Server sends the connection preface, including the initial SETTINGS. EXPECT_CALL(server_visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x0)); EXPECT_CALL(server_visitor, OnFrameSent(SETTINGS, 0, 0, 0x0, 0)); { int result = server_adapter->Send(); EXPECT_EQ(0, result); absl::string_view data = server_visitor.data(); EXPECT_THAT(data, EqualsFrames({SpdyFrameType::SETTINGS})); } // Client processes the server's initial bytes, including initial SETTINGS. EXPECT_CALL(client_visitor, OnFrameHeader(0, 0, SETTINGS, 0x0)); EXPECT_CALL(client_visitor, OnSettingsStart()); EXPECT_CALL(client_visitor, OnSettingsEnd()); { const int64_t result = client_adapter->ProcessBytes(server_visitor.data()); EXPECT_EQ(server_visitor.data().size(), static_cast(result)); } // Server processes the client's initial bytes, including initial SETTINGS. EXPECT_CALL(server_visitor, OnFrameHeader(0, 12, SETTINGS, 0x0)); EXPECT_CALL(server_visitor, OnSettingsStart()); EXPECT_CALL(server_visitor, OnSetting(Http2Setting{Http2KnownSettingsId::ENABLE_PUSH, 0u})) .Times(2); EXPECT_CALL( server_visitor, OnSetting(Http2Setting{Http2KnownSettingsId::MAX_HEADER_LIST_SIZE, 42u})) .Times(2); EXPECT_CALL(server_visitor, OnSettingsEnd()); { const int64_t result = server_adapter->ProcessBytes(client_visitor.data()); EXPECT_EQ(client_visitor.data().size(), static_cast(result)); } } TEST(OgHttp2AdapterTest, InitialSettings) { DataSavingVisitor client_visitor; OgHttp2Adapter::Options client_options{.perspective = Perspective::kClient, .max_header_list_bytes = 42}; ASSERT_TRUE(client_options.allow_extended_connect); auto client_adapter = OgHttp2Adapter::Create(client_visitor, client_options); DataSavingVisitor server_visitor; OgHttp2Adapter::Options server_options{.perspective = Perspective::kServer}; ASSERT_TRUE(server_options.allow_extended_connect); auto server_adapter = OgHttp2Adapter::Create(server_visitor, server_options); testing::InSequence s; // Client sends the connection preface, including the initial SETTINGS. EXPECT_CALL(client_visitor, OnBeforeFrameSent(SETTINGS, 0, 12, 0x0)); EXPECT_CALL(client_visitor, OnFrameSent(SETTINGS, 0, 12, 0x0, 0)); { int result = client_adapter->Send(); EXPECT_EQ(0, result); absl::string_view data = client_visitor.data(); EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); EXPECT_THAT(data, EqualsFrames({SpdyFrameType::SETTINGS})); } // Server sends the connection preface, including the initial SETTINGS. EXPECT_CALL(server_visitor, OnBeforeFrameSent(SETTINGS, 0, 6, 0x0)); EXPECT_CALL(server_visitor, OnFrameSent(SETTINGS, 0, 6, 0x0, 0)); { int result = server_adapter->Send(); EXPECT_EQ(0, result); absl::string_view data = server_visitor.data(); EXPECT_THAT(data, EqualsFrames({SpdyFrameType::SETTINGS})); } // Client processes the server's initial bytes, including initial SETTINGS. EXPECT_CALL(client_visitor, OnFrameHeader(0, 6, SETTINGS, 0x0)); EXPECT_CALL(client_visitor, OnSettingsStart()); EXPECT_CALL(client_visitor, OnSetting(Http2Setting{ Http2KnownSettingsId::ENABLE_CONNECT_PROTOCOL, 1u})) .Times(2); EXPECT_CALL(client_visitor, OnSettingsEnd()); { const int64_t result = client_adapter->ProcessBytes(server_visitor.data()); EXPECT_EQ(server_visitor.data().size(), static_cast(result)); } // Server processes the client's initial bytes, including initial SETTINGS. EXPECT_CALL(server_visitor, OnFrameHeader(0, 12, SETTINGS, 0x0)); EXPECT_CALL(server_visitor, OnSettingsStart()); EXPECT_CALL(server_visitor, OnSetting(Http2Setting{Http2KnownSettingsId::ENABLE_PUSH, 0u})) .Times(2); EXPECT_CALL( server_visitor, OnSetting(Http2Setting{Http2KnownSettingsId::MAX_HEADER_LIST_SIZE, 42u})) .Times(2); EXPECT_CALL(server_visitor, OnSettingsEnd()); { const int64_t result = server_adapter->ProcessBytes(client_visitor.data()); EXPECT_EQ(client_visitor.data().size(), static_cast(result)); } } TEST(OgHttp2AdapterTest, AutomaticSettingsAndPingAcks) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, 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(read_result), frames.size()); EXPECT_TRUE(adapter->want_write()); // Server preface (SETTINGS) EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); // 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({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, SpdyFrameType::PING})); } TEST(OgHttp2AdapterTest, AutomaticPingAcksDisabled) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer, .auto_ping_ack = false}; auto adapter = OgHttp2Adapter::Create(visitor, 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(read_result), frames.size()); EXPECT_TRUE(adapter->want_write()); // Server preface (SETTINGS) EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); // 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({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS})); } TEST(OgHttp2AdapterTest, ClientHandles100Headers) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
headers1 = ToHeaders({{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}); const int32_t stream_id1 = adapter->SubmitRequest(headers1, nullptr, nullptr); ASSERT_GT(stream_id1, 0); QUICHE_LOG(INFO) << "Created stream: " << stream_id1; EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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() .Headers(1, {{":status", "100"}}, /*fin=*/false) .Ping(101) .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, 4)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "100")); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnFrameHeader(0, 8, PING, 0)); EXPECT_CALL(visitor, OnPing(101, false)); 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")); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnEndStream(1)); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), static_cast(stream_result)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(PING, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(PING, 0, _, 0x1, 0)); EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::PING})); } TEST(OgHttp2AdapterTest, ClientRejects100HeadersWithFin) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
headers1 = ToHeaders({{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}); const int32_t stream_id1 = adapter->SubmitRequest(headers1, nullptr, nullptr); ASSERT_GT(stream_id1, 0); QUICHE_LOG(INFO) << "Created stream: " << stream_id1; EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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() .Headers(1, {{":status", "100"}}, /*fin=*/false) .Headers(1, {{":status", "100"}}, /*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", "100")); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "100")); EXPECT_CALL(visitor, OnInvalidFrame( 1, Http2VisitorInterface::InvalidFrameError::kHttpMessaging)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), static_cast(stream_result)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 1, _, 0x0, 1)); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::RST_STREAM})); } TEST(OgHttp2AdapterTest, ClientRejects100HeadersWithContent) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
headers1 = ToHeaders({{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}); const int32_t stream_id1 = adapter->SubmitRequest(headers1, nullptr, nullptr); ASSERT_GT(stream_id1, 0); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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() .Headers(1, {{":status", "100"}}, /*fin=*/false) .Data(1, "We needed the final headers before data, whoops") .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", "100")); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnFrameHeader(1, _, DATA, 0)); EXPECT_CALL(visitor, OnBeginDataForStream(1, _)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), static_cast(stream_result)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 1, _, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::RST_STREAM})); } TEST(OgHttp2AdapterTest, ClientRejects100HeadersWithContentLength) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
headers1 = ToHeaders({{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}); const int32_t stream_id1 = adapter->SubmitRequest(headers1, nullptr, nullptr); ASSERT_GT(stream_id1, 0); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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() .Headers(1, {{":status", "100"}, {"content-length", "42"}}, /*fin=*/false) .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, 4)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "100")); EXPECT_CALL( visitor, OnInvalidFrame(1, Http2VisitorInterface::InvalidFrameError::kHttpHeader)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), static_cast(stream_result)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 1, _, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::RST_STREAM})); } TEST(OgHttp2AdapterTest, ClientHandles204WithContent) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
headers1 = ToHeaders({{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}); const int32_t stream_id1 = adapter->SubmitRequest(headers1, nullptr, nullptr); ASSERT_GT(stream_id1, 0); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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() .Headers(1, {{":status", "204"}, {"content-length", "2"}}, /*fin=*/false) .Data(1, "hi") .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", "204")); EXPECT_CALL( visitor, OnInvalidFrame(1, Http2VisitorInterface::InvalidFrameError::kHttpHeader)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), static_cast(stream_result)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 1, _, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::RST_STREAM})); } TEST(OgHttp2AdapterTest, ClientHandles304WithContent) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
headers1 = ToHeaders({{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}); const int32_t stream_id1 = adapter->SubmitRequest(headers1, nullptr, nullptr); ASSERT_GT(stream_id1, 0); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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() .Headers(1, {{":status", "304"}, {"content-length", "2"}}, /*fin=*/false) .Data(1, "hi") .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", "304")); EXPECT_CALL(visitor, OnHeaderForStream(1, "content-length", "2")); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnFrameHeader(1, _, DATA, 0)); EXPECT_CALL(visitor, OnBeginDataForStream(1, 2)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), static_cast(stream_result)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 1, _, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::RST_STREAM})); } TEST(OgHttp2AdapterTest, ClientHandles304WithContentLength) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
headers = ToHeaders({{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}); const int32_t stream_id = adapter->SubmitRequest(headers, nullptr, nullptr); ASSERT_GT(stream_id, 0); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x5)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x5, 0)); int result = adapter->Send(); EXPECT_EQ(0, result); visitor.Clear(); const std::string stream_frames = TestFrameSequence() .ServerPreface() .Headers(1, {{":status", "304"}, {"content-length", "2"}}, /*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", "304")); EXPECT_CALL(visitor, OnHeaderForStream(1, "content-length", "2")); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnEndStream(1)); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), static_cast(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({SpdyFrameType::SETTINGS})); } TEST(OgHttp2AdapterTest, ClientHandlesTrailers) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
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(kSentinel1)); ASSERT_GT(stream_id1, 0); QUICHE_LOG(INFO) << "Created stream: " << stream_id1; EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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({SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS})); visitor.Clear(); const std::string stream_frames = TestFrameSequence() .ServerPreface() .Headers(1, {{":status", "200"}, {"server", "my-fake-server"}, {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, /*fin=*/false) .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::HTTP2_NO_ERROR)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), static_cast(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({SpdyFrameType::SETTINGS})); } TEST(OgHttp2AdapterTest, ClientSendsTrailers) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
headers1 = ToHeaders({{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}); const std::string kBody = "This is an example request body."; auto body1 = absl::make_unique(visitor, false); body1->AppendPayload(kBody); body1->EndData(); const int32_t stream_id1 = adapter->SubmitRequest(headers1, std::move(body1), nullptr); ASSERT_GT(stream_id1, 0); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x4)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x4, 0)); EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id1, _, 0x0, 0)); int result = adapter->Send(); EXPECT_EQ(0, result); absl::string_view data = visitor.data(); EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); EXPECT_THAT(data, EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS, SpdyFrameType::DATA})); visitor.Clear(); const std::vector
trailers1 = ToHeaders({{"extra-info", "Trailers are weird but good?"}}); adapter->SubmitTrailer(stream_id1, trailers1); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0)); result = adapter->Send(); EXPECT_EQ(0, result); data = visitor.data(); EXPECT_THAT(data, EqualsFrames({SpdyFrameType::HEADERS})); } TEST(OgHttp2AdapterTest, ClientHandlesMetadata) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
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(kSentinel1)); ASSERT_GT(stream_id1, 0); QUICHE_LOG(INFO) << "Created stream: " << stream_id1; EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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({SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS})); visitor.Clear(); const std::string stream_frames = TestFrameSequence() .ServerPreface() .Metadata(0, "Example connection metadata") .Headers(1, {{":status", "200"}, {"server", "my-fake-server"}, {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, /*fin=*/false) .Metadata(1, "Example stream metadata") .Data(1, "This is the response body.", true) .Serialize(); // Server preface (empty SETTINGS) EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); EXPECT_CALL(visitor, OnSettingsStart()); EXPECT_CALL(visitor, OnSettingsEnd()); EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 4)); EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _)); EXPECT_CALL(visitor, OnMetadataForStream(0, _)); EXPECT_CALL(visitor, OnMetadataEndForStream(0)); EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200")); EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server")); EXPECT_CALL(visitor, OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT")); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 4)); EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _)); EXPECT_CALL(visitor, OnMetadataForStream(1, _)); EXPECT_CALL(visitor, OnMetadataEndForStream(1)); EXPECT_CALL(visitor, OnFrameHeader(1, 26, DATA, 1)); EXPECT_CALL(visitor, OnBeginDataForStream(1, 26)); EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body.")); EXPECT_CALL(visitor, OnEndStream(1)); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), static_cast(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({SpdyFrameType::SETTINGS})); } TEST(OgHttp2AdapterTest, ClientHandlesMetadataWithEmptyPayload) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
headers1 = ToHeaders({{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}); const int32_t stream_id = adapter->SubmitRequest(headers1, nullptr, nullptr); ASSERT_GT(stream_id, 0); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x5)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 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({SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS})); visitor.Clear(); const std::string stream_frames = TestFrameSequence() .ServerPreface() .Headers(1, {{":status", "200"}, {"server", "my-fake-server"}, {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, /*fin=*/false) .Metadata(1, "") .Data(1, "This is the response body.", true) .Serialize(); // Server preface (empty SETTINGS) EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); EXPECT_CALL(visitor, OnSettingsStart()); EXPECT_CALL(visitor, OnSettingsEnd()); EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(3); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 4)); EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _)); EXPECT_CALL(visitor, OnMetadataEndForStream(1)); EXPECT_CALL(visitor, OnFrameHeader(1, _, DATA, 1)); EXPECT_CALL(visitor, OnBeginDataForStream(1, _)); EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body.")); EXPECT_CALL(visitor, OnEndStream(1)); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), static_cast(stream_result)); } TEST(OgHttp2AdapterTest, ClientHandlesMetadataWithPayloadError) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
headers = ToHeaders({{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}); const int32_t stream_id = adapter->SubmitRequest(headers, nullptr, nullptr); ASSERT_GT(stream_id, 0); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x5)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 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(stream_id, {{":status", "200"}, {"server", "my-fake-server"}, {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, /*fin=*/false) .Metadata(stream_id, "Example stream metadata") .Data(stream_id, "This is the response body.", true) .Serialize(); // Server preface (empty SETTINGS) EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); EXPECT_CALL(visitor, OnSettingsStart()); EXPECT_CALL(visitor, OnSettingsEnd()); EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 4)); EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _)); EXPECT_CALL(visitor, OnMetadataForStream(0, _)); EXPECT_CALL(visitor, OnMetadataEndForStream(0)); EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, HEADERS, 4)); EXPECT_CALL(visitor, OnBeginHeadersForStream(stream_id)); EXPECT_CALL(visitor, OnHeaderForStream(stream_id, _, _)).Times(3); EXPECT_CALL(visitor, OnEndHeadersForStream(stream_id)); EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, kMetadataFrameType, 4)); EXPECT_CALL(visitor, OnBeginMetadataForStream(stream_id, _)); EXPECT_CALL(visitor, OnMetadataForStream(stream_id, _)) .WillOnce(testing::Return(false)); // Remaining frames are not processed due to the error. EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kParseError)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); // Negative integer returned to indicate an error. EXPECT_LT(stream_result, 0); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); EXPECT_FALSE(adapter->want_read()); EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS})); } TEST(OgHttp2AdapterTest, ClientHandlesMetadataWithCompletionError) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
headers = ToHeaders({{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}); const int32_t stream_id = adapter->SubmitRequest(headers, nullptr, nullptr); ASSERT_GT(stream_id, 0); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x5)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 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(stream_id, {{":status", "200"}, {"server", "my-fake-server"}, {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, /*fin=*/false) .Metadata(stream_id, "Example stream metadata") .Data(stream_id, "This is the response body.", true) .Serialize(); // Server preface (empty SETTINGS) EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); EXPECT_CALL(visitor, OnSettingsStart()); EXPECT_CALL(visitor, OnSettingsEnd()); EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 4)); EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _)); EXPECT_CALL(visitor, OnMetadataForStream(0, _)); EXPECT_CALL(visitor, OnMetadataEndForStream(0)); EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, HEADERS, 4)); EXPECT_CALL(visitor, OnBeginHeadersForStream(stream_id)); EXPECT_CALL(visitor, OnHeaderForStream(stream_id, _, _)).Times(3); EXPECT_CALL(visitor, OnEndHeadersForStream(stream_id)); EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, kMetadataFrameType, 4)); EXPECT_CALL(visitor, OnBeginMetadataForStream(stream_id, _)); EXPECT_CALL(visitor, OnMetadataForStream(stream_id, _)); EXPECT_CALL(visitor, OnMetadataEndForStream(stream_id)) .WillOnce(testing::Return(false)); // Remaining frames are not processed due to the error. EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kParseError)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); // Negative integer returned to indicate an error. EXPECT_LT(stream_result, 0); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); EXPECT_FALSE(adapter->want_read()); EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS})); } TEST(OgHttp2AdapterTest, ClientRstStreamWhileHandlingHeaders) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
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(kSentinel1)); ASSERT_GT(stream_id1, 0); QUICHE_LOG(INFO) << "Created stream: " << stream_id1; EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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({SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS})); visitor.Clear(); const std::string stream_frames = TestFrameSequence() .ServerPreface() .Headers(1, {{":status", "200"}, {"server", "my-fake-server"}, {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, /*fin=*/false) .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(), static_cast(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(Http2ErrorCode::REFUSED_STREAM))); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::RST_STREAM})); } TEST(OgHttp2AdapterTest, ClientConnectionErrorWhileHandlingHeaders) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
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(kSentinel1)); ASSERT_GT(stream_id1, 0); QUICHE_LOG(INFO) << "Created stream: " << stream_id1; EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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({SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS})); visitor.Clear(); const std::string stream_frames = TestFrameSequence() .ServerPreface() .Headers(1, {{":status", "200"}, {"server", "my-fake-server"}, {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, /*fin=*/false) .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)); EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kHeaderError)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_LT(stream_result, 0); 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({SpdyFrameType::SETTINGS})); } TEST(OgHttp2AdapterTest, ClientConnectionErrorWhileHandlingHeadersOnly) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
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(kSentinel1)); ASSERT_GT(stream_id1, 0); QUICHE_LOG(INFO) << "Created stream: " << stream_id1; EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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({SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS})); visitor.Clear(); const std::string stream_frames = TestFrameSequence() .ServerPreface() .Headers(1, {{":status", "200"}, {"server", "my-fake-server"}, {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, /*fin=*/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)); EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kHeaderError)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_LT(stream_result, 0); 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({SpdyFrameType::SETTINGS})); } TEST(OgHttp2AdapterTest, ClientRejectsHeaders) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
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(kSentinel1)); ASSERT_GT(stream_id1, 0); QUICHE_LOG(INFO) << "Created stream: " << stream_id1; EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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({SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS})); visitor.Clear(); const std::string stream_frames = TestFrameSequence() .ServerPreface() .Headers(1, {{":status", "200"}, {"server", "my-fake-server"}, {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, /*fin=*/false) .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::kHeaderError)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_LT(stream_result, 0); 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({SpdyFrameType::SETTINGS})); } TEST(OgHttp2AdapterTest, ClientHandlesSmallerHpackHeaderTableSetting) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
headers1 = ToHeaders({ {":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}, {"x-i-do-not-like", "green eggs and ham"}, {"x-i-will-not-eat-them", "here or there, in a box, with a fox"}, {"x-like-them-in-a-house", "no"}, {"x-like-them-with-a-mouse", "no"}, }); const int32_t stream_id1 = adapter->SubmitRequest(headers1, nullptr, nullptr); ASSERT_GT(stream_id1, 0); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0)); int result = adapter->Send(); EXPECT_EQ(0, result); visitor.Clear(); EXPECT_GT(adapter->GetHpackEncoderDynamicTableSize(), 100); const std::string stream_frames = TestFrameSequence().Settings({{HEADER_TABLE_SIZE, 100u}}).Serialize(); // Server preface (SETTINGS) EXPECT_CALL(visitor, OnFrameHeader(0, 6, SETTINGS, 0)); EXPECT_CALL(visitor, OnSettingsStart()); EXPECT_CALL(visitor, OnSetting(Http2Setting{HEADER_TABLE_SIZE, 100u})); // Duplicate setting callback due to the way extensions work. EXPECT_CALL(visitor, OnSetting(Http2Setting{HEADER_TABLE_SIZE, 100u})); EXPECT_CALL(visitor, OnSettingsEnd()); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), static_cast(stream_result)); EXPECT_EQ(adapter->GetHpackEncoderDynamicTableCapacity(), 100); EXPECT_LE(adapter->GetHpackEncoderDynamicTableSize(), 100); } TEST(OgHttp2AdapterTest, ClientHandlesLargerHpackHeaderTableSetting) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); int result = adapter->Send(); EXPECT_EQ(0, result); visitor.Clear(); EXPECT_EQ(adapter->GetHpackEncoderDynamicTableCapacity(), 4096); const std::string stream_frames = TestFrameSequence().Settings({{HEADER_TABLE_SIZE, 40960u}}).Serialize(); // Server preface (SETTINGS) EXPECT_CALL(visitor, OnFrameHeader(0, 6, SETTINGS, 0)); EXPECT_CALL(visitor, OnSettingsStart()); EXPECT_CALL(visitor, OnSetting(Http2Setting{HEADER_TABLE_SIZE, 40960u})); // Duplicate setting callback due to the way extensions work. EXPECT_CALL(visitor, OnSetting(Http2Setting{HEADER_TABLE_SIZE, 40960u})); EXPECT_CALL(visitor, OnSettingsEnd()); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), static_cast(stream_result)); // The increased capacity will not be applied until a SETTINGS ack is // serialized. EXPECT_EQ(adapter->GetHpackEncoderDynamicTableCapacity(), 4096); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); result = adapter->Send(); EXPECT_EQ(0, result); visitor.Clear(); EXPECT_EQ(adapter->GetHpackEncoderDynamicTableCapacity(), 40960); } TEST(OgHttp2AdapterTest, ClientSendsHpackHeaderTableSetting) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
headers1 = ToHeaders({ {":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}, }); const int32_t stream_id1 = adapter->SubmitRequest(headers1, nullptr, nullptr); ASSERT_GT(stream_id1, 0); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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() .SettingsAck() .Headers( 1, {{":status", "200"}, {"server", "my-fake-server"}, {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}, {"x-i-do-not-like", "green eggs and ham"}, {"x-i-will-not-eat-them", "here or there, in a box, with a fox"}, {"x-like-them-in-a-house", "no"}, {"x-like-them-with-a-mouse", "no"}}, /*fin=*/true) .Serialize(); // Server preface (empty SETTINGS) EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); EXPECT_CALL(visitor, OnSettingsStart()); EXPECT_CALL(visitor, OnSettingsEnd()); // Server acks client's initial SETTINGS. EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 1)); EXPECT_CALL(visitor, OnSettingsAck()); EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(7); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnEndStream(1)); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), static_cast(stream_result)); EXPECT_GT(adapter->GetHpackDecoderSizeLimit(), 100); // Submit settings, check decoder table size. adapter->SubmitSettings({{HEADER_TABLE_SIZE, 100u}}); EXPECT_GT(adapter->GetHpackDecoderSizeLimit(), 100); // Server preface SETTINGS ack EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); // SETTINGS with the new header table size value EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 6, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 6, 0x0, 0)); // Because the client has not yet seen an ack from the server for the SETTINGS // with header table size, it has not applied the new value. EXPECT_GT(adapter->GetHpackDecoderSizeLimit(), 100); result = adapter->Send(); EXPECT_EQ(0, result); visitor.Clear(); const std::vector
headers2 = ToHeaders({ {":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/two"}, }); const int32_t stream_id2 = adapter->SubmitRequest(headers2, nullptr, nullptr); ASSERT_GT(stream_id2, stream_id1); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id2, _, 0x5)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id2, _, 0x5, 0)); result = adapter->Send(); EXPECT_EQ(0, result); visitor.Clear(); const std::string response_frames = TestFrameSequence() .Headers(stream_id2, {{":status", "200"}, {"server", "my-fake-server"}, {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, /*fin=*/true) .Serialize(); EXPECT_CALL(visitor, OnFrameHeader(stream_id2, _, HEADERS, 5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(stream_id2)); EXPECT_CALL(visitor, OnHeaderForStream(stream_id2, _, _)).Times(3); EXPECT_CALL(visitor, OnEndHeadersForStream(stream_id2)); EXPECT_CALL(visitor, OnEndStream(stream_id2)); EXPECT_CALL(visitor, OnCloseStream(stream_id2, Http2ErrorCode::HTTP2_NO_ERROR)); const int64_t response_result = adapter->ProcessBytes(response_frames); EXPECT_EQ(response_frames.size(), static_cast(response_result)); // Still no ack for the outbound settings. EXPECT_GT(adapter->GetHpackDecoderSizeLimit(), 100); const std::string settings_ack = TestFrameSequence().SettingsAck().Serialize(); EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 1)); EXPECT_CALL(visitor, OnSettingsAck()); const int64_t ack_result = adapter->ProcessBytes(settings_ack); EXPECT_EQ(settings_ack.size(), static_cast(ack_result)); // Ack has finally arrived. EXPECT_EQ(adapter->GetHpackDecoderSizeLimit(), 100); } // TODO(birenroy): Validate headers and re-enable this test. The library should // invoke OnErrorDebug() with an error message for the invalid header. The // library should also invoke OnInvalidFrame() for the invalid HEADERS frame. TEST(OgHttp2AdapterTest, DISABLED_ClientHandlesInvalidTrailers) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
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(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({SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS})); visitor.Clear(); const std::string stream_frames = TestFrameSequence() .ServerPreface() .Headers(1, {{":status", "200"}, {"server", "my-fake-server"}, {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, /*fin=*/false) .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)); // 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(), static_cast(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({SpdyFrameType::SETTINGS, SpdyFrameType::RST_STREAM})); } TEST(OgHttp2AdapterTest, ClientStartsShutdown) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); EXPECT_FALSE(adapter->want_write()); // No-op (except for logging) for a client implementation. adapter->SubmitShutdownNotice(); EXPECT_FALSE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 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({SpdyFrameType::SETTINGS})); } TEST(OgHttp2AdapterTest, ClientFailsOnGoAway) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
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(kSentinel1)); ASSERT_GT(stream_id1, 0); QUICHE_LOG(INFO) << "Created stream: " << stream_id1; EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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({SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS})); visitor.Clear(); const std::string stream_frames = TestFrameSequence() .ServerPreface() .Headers(1, {{":status", "200"}, {"server", "my-fake-server"}, {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, /*fin=*/false) .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)); // TODO(birenroy): Pass the GOAWAY opaque data through the oghttp2 stack. EXPECT_CALL(visitor, OnGoAway(1, Http2ErrorCode::INTERNAL_ERROR, "")) .WillOnce(testing::Return(false)); EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kParseError)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_LT(stream_result, 0); 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({SpdyFrameType::SETTINGS})); } TEST(OgHttp2AdapterTest, ClientRejects101Response) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
headers1 = ToHeaders({{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}, {"upgrade", "new-protocol"}}); const int32_t stream_id1 = adapter->SubmitRequest(headers1, nullptr, nullptr); ASSERT_GT(stream_id1, 0); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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({SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS})); visitor.Clear(); const std::string stream_frames = TestFrameSequence() .ServerPreface() .Headers(1, {{":status", "101"}, {"server", "my-fake-server"}, {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, /*fin=*/false) .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, OnInvalidFrame(1, Http2VisitorInterface::InvalidFrameError::kHttpHeader)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(static_cast(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, 1, 4, 0x0)); EXPECT_CALL( visitor, OnFrameSent(RST_STREAM, 1, 4, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::RST_STREAM})); } TEST(OgHttp2AdapterTest, ClientObeysMaxConcurrentStreams) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); EXPECT_FALSE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); // Even though the user has not queued any frames for the session, it should // still send the connection preface. 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)); // Initial SETTINGS. EXPECT_THAT(serialized, EqualsFrames({SpdyFrameType::SETTINGS})); 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); // TODO(diannahu): Remove this duplicate call with a separate // ExtensionVisitorInterface implementation. EXPECT_CALL(visitor, OnSetting); EXPECT_CALL(visitor, OnSettingsEnd()); const int64_t initial_result = adapter->ProcessBytes(initial_frames); EXPECT_EQ(initial_frames.size(), static_cast(initial_result)); // Session will want to write a SETTINGS ack. EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS})); visitor.Clear(); const std::string kBody = "This is an example request body."; auto body1 = absl::make_unique(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({SpdyFrameType::HEADERS, 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::HTTP2_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(), static_cast(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({SpdyFrameType::HEADERS})); visitor.Clear(); EXPECT_FALSE(adapter->want_write()); } TEST(OgHttp2AdapterTest, ClientReceivesInitialWindowSetting) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); const std::string initial_frames = TestFrameSequence() .Settings({{INITIAL_WINDOW_SIZE, 80000u}}) .WindowUpdate(0, 65536) .Serialize(); // Server preface (SETTINGS with INITIAL_STREAM_WINDOW) EXPECT_CALL(visitor, OnFrameHeader(0, 6, SETTINGS, 0)); EXPECT_CALL(visitor, OnSettingsStart()); // TODO(diannahu): Remove the duplicate call with a separate // ExtensionVisitorInterface implementation. EXPECT_CALL(visitor, OnSetting(Http2Setting{INITIAL_WINDOW_SIZE, 80000u})) .Times(2); EXPECT_CALL(visitor, OnSettingsEnd()); EXPECT_CALL(visitor, OnFrameHeader(0, 4, WINDOW_UPDATE, 0)); EXPECT_CALL(visitor, OnWindowUpdate(0, 65536)); const int64_t initial_result = adapter->ProcessBytes(initial_frames); EXPECT_EQ(initial_frames.size(), static_cast(initial_result)); // Session will want to write a SETTINGS ack. EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); int64_t 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({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS})); visitor.Clear(); const std::string kLongBody = std::string(81000, 'c'); auto body1 = absl::make_unique(visitor, true); body1->AppendPayload(kLongBody); 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)); // The client can send more than 4 frames (65536 bytes) of data. EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, 16384, 0x0, 0)).Times(4); EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, 14464, 0x0, 0)); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::HEADERS, SpdyFrameType::DATA, SpdyFrameType::DATA, SpdyFrameType::DATA, SpdyFrameType::DATA, SpdyFrameType::DATA})); } TEST(OgHttp2AdapterTest, ClientReceivesInitialWindowSettingAfterStreamStart) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); const std::string initial_frames = TestFrameSequence().ServerPreface().WindowUpdate(0, 65536).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, 4, WINDOW_UPDATE, 0)); EXPECT_CALL(visitor, OnWindowUpdate(0, 65536)); const int64_t initial_result = adapter->ProcessBytes(initial_frames); EXPECT_EQ(initial_frames.size(), static_cast(initial_result)); // Session will want to write a SETTINGS ack. EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); int64_t result = adapter->Send(); EXPECT_EQ(0, result); visitor.Clear(); const std::string kLongBody = std::string(81000, 'c'); auto body1 = absl::make_unique(visitor, true); body1->AppendPayload(kLongBody); 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)); // The client can only send 65535 bytes of data, as the stream window has not // yet been increased. EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, 16384, 0x0, 0)).Times(3); EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, 16383, 0x0, 0)); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::HEADERS, SpdyFrameType::DATA, SpdyFrameType::DATA, SpdyFrameType::DATA, SpdyFrameType::DATA})); visitor.Clear(); // Can't write any more due to flow control. EXPECT_FALSE(adapter->want_write()); const std::string settings_frame = TestFrameSequence().Settings({{INITIAL_WINDOW_SIZE, 80000u}}).Serialize(); // SETTINGS with INITIAL_STREAM_WINDOW EXPECT_CALL(visitor, OnFrameHeader(0, 6, SETTINGS, 0)); EXPECT_CALL(visitor, OnSettingsStart()); // TODO(diannahu): Remove the duplicate call with a separate // ExtensionVisitorInterface implementation. EXPECT_CALL(visitor, OnSetting(Http2Setting{INITIAL_WINDOW_SIZE, 80000u})) .Times(2); EXPECT_CALL(visitor, OnSettingsEnd()); const int64_t settings_result = adapter->ProcessBytes(settings_frame); EXPECT_EQ(settings_frame.size(), static_cast(settings_result)); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); // The client can write more after receiving the INITIAL_WINDOW_SIZE setting. EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, 14465, 0x0, 0)); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::DATA})); } TEST(OgHttp2AdapterTest, InvalidInitialWindowSetting) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); const uint32_t kTooLargeInitialWindow = 1u << 31; const std::string initial_frames = TestFrameSequence() .Settings({{INITIAL_WINDOW_SIZE, kTooLargeInitialWindow}}) .Serialize(); // Server preface (SETTINGS with INITIAL_STREAM_WINDOW) EXPECT_CALL(visitor, OnFrameHeader(0, 6, SETTINGS, 0)); EXPECT_CALL(visitor, OnSettingsStart()); // TODO(diannahu): Remove the duplicate call with a separate // ExtensionVisitorInterface implementation. EXPECT_CALL( visitor, OnInvalidFrame(0, Http2VisitorInterface::InvalidFrameError::kFlowControl)) .Times(2); EXPECT_CALL(visitor, OnConnectionError( Http2VisitorInterface::ConnectionError::kFlowControlError)); const int64_t initial_result = adapter->ProcessBytes(initial_frames); EXPECT_EQ(initial_frames.size(), static_cast(initial_result)); // Session will want to write a GOAWAY. EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); int64_t 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({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS})); visitor.Clear(); } TEST(OggHttp2AdapterClientTest, InitialWindowSettingCausesOverflow) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
headers = ToHeaders({{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}); const int32_t stream_id = adapter->SubmitRequest(headers, nullptr, nullptr); ASSERT_GT(stream_id, 0); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x5)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x5, 0)); int64_t 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({SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS})); visitor.Clear(); const uint32_t kLargeInitialWindow = (1u << 31) - 1; const std::string frames = TestFrameSequence() .ServerPreface() .Headers(stream_id, {{":status", "200"}}, /*fin=*/false) .WindowUpdate(stream_id, 65536u) .Settings({{INITIAL_WINDOW_SIZE, kLargeInitialWindow}}) .Serialize(); EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); EXPECT_CALL(visitor, OnSettingsStart()); EXPECT_CALL(visitor, OnSettingsEnd()); EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, HEADERS, 0x4)); EXPECT_CALL(visitor, OnBeginHeadersForStream(stream_id)); EXPECT_CALL(visitor, OnHeaderForStream(stream_id, ":status", "200")); EXPECT_CALL(visitor, OnEndHeadersForStream(stream_id)); EXPECT_CALL(visitor, OnFrameHeader(stream_id, 4, WINDOW_UPDATE, 0x0)); EXPECT_CALL(visitor, OnWindowUpdate(stream_id, 65536)); EXPECT_CALL(visitor, OnFrameHeader(0, 6, SETTINGS, 0)); EXPECT_CALL(visitor, OnSettingsStart()); EXPECT_CALL(visitor, OnSetting(Http2Setting{INITIAL_WINDOW_SIZE, kLargeInitialWindow})) .Times(2); EXPECT_CALL(visitor, OnSettingsEnd()); const int64_t read_result = adapter->ProcessBytes(frames); EXPECT_EQ(static_cast(read_result), frames.size()); 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(SETTINGS, 0, 0, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); // The stream window update plus the SETTINGS frame with INITIAL_WINDOW_SIZE // pushes the stream's flow control window outside of the acceptable range. EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, stream_id, 4, 0x0)); EXPECT_CALL( visitor, OnFrameSent(RST_STREAM, stream_id, 4, 0x0, static_cast(Http2ErrorCode::FLOW_CONTROL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(stream_id, Http2ErrorCode::HTTP2_NO_ERROR)); int result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, SpdyFrameType::RST_STREAM})); } TEST(OgHttp2AdapterTest, FailureSendingConnectionPreface) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); visitor.set_has_write_error(); EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kSendError)); int result = adapter->Send(); EXPECT_LT(result, 0); } TEST(OgHttp2AdapterTest, ClientForbidsPushPromise) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 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({SpdyFrameType::SETTINGS})); visitor.Clear(); const std::vector
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({SpdyFrameType::HEADERS})); visitor.Clear(); const std::vector
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, though this is not explicitly // required for OgHttp2: should it be?) EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0x1)); EXPECT_CALL(visitor, OnSettingsAck); // The PUSH_PROMISE is treated as an invalid frame. EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, PUSH_PROMISE, _)); EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kInvalidPushPromise)); const int64_t read_result = adapter->ProcessBytes(frames); EXPECT_EQ(static_cast(read_result), frames.size()); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); int result = adapter->Send(); EXPECT_EQ(0, result); // SETTINGS ack. EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS})); } TEST(OgHttp2AdapterTest, ClientForbidsPushStream) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 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({SpdyFrameType::SETTINGS})); visitor.Clear(); const std::vector
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({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, though this is not explicitly // required for OgHttp2: should it be?) 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, OnConnectionError(ConnectionError::kInvalidNewStreamId)); const int64_t read_result = adapter->ProcessBytes(frames); EXPECT_EQ(static_cast(read_result), frames.size()); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); int result = adapter->Send(); EXPECT_EQ(0, result); // SETTINGS ack. EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS})); } TEST(OgHttp2AdapterTest, ClientReceivesDataOnClosedStream) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); int result = adapter->Send(); EXPECT_EQ(0, result); absl::string_view data = visitor.data(); EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); EXPECT_THAT(data, EqualsFrames({SpdyFrameType::SETTINGS})); 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(), static_cast(initial_result)); // Client SETTINGS ack 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({SpdyFrameType::SETTINGS})); visitor.Clear(); // Let the client open a stream with a request. int stream_id = adapter->SubmitRequest(ToHeaders({{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}), nullptr, nullptr); EXPECT_GT(stream_id, 0); EXPECT_TRUE(adapter->want_write()); 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({SpdyFrameType::HEADERS})); visitor.Clear(); // Let the client RST_STREAM the stream it opened. adapter->SubmitRst(stream_id, Http2ErrorCode::CANCEL); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, stream_id, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, stream_id, _, 0x0, static_cast(Http2ErrorCode::CANCEL))); EXPECT_CALL(visitor, OnCloseStream(stream_id, Http2ErrorCode::HTTP2_NO_ERROR)); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::RST_STREAM})); visitor.Clear(); // Let the server send a response on the stream. (It might not have received // the RST_STREAM yet.) const std::string response_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(); // The visitor gets notified about the HEADERS frame and DATA frame for the // closed stream with no further processing on either frame. EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, HEADERS, 0x4)); EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, DATA, 0x1)); const int64_t response_result = adapter->ProcessBytes(response_frames); EXPECT_EQ(response_frames.size(), static_cast(response_result)); EXPECT_FALSE(adapter->want_write()); } TEST(OgHttp2AdapterTest, ClientEncountersFlowControlBlock) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
headers1 = ToHeaders({{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}); const std::string kBody = std::string(100 * 1024, 'a'); auto body1 = absl::make_unique(visitor, false); body1->AppendPayload(kBody); body1->EndData(); const int32_t stream_id1 = adapter->SubmitRequest(headers1, std::move(body1), nullptr); ASSERT_GT(stream_id1, 0); const std::vector
headers2 = ToHeaders({{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/two"}}); auto body2 = absl::make_unique(visitor, false); body2->AppendPayload(kBody); body2->EndData(); const int32_t stream_id2 = adapter->SubmitRequest(headers2, std::move(body2), nullptr); ASSERT_GT(stream_id2, 0); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x4)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x4, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id2, _, 0x4)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id2, _, 0x4, 0)); // 4 DATA frames should saturate the default 64kB stream/connection flow // control window. EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id1, _, 0x0, 0)).Times(4); int result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_EQ(0, adapter->GetSendWindowSize()); const std::string stream_frames = TestFrameSequence() .ServerPreface() .WindowUpdate(0, 80000) .WindowUpdate(stream_id1, 20000) .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, 4, WINDOW_UPDATE, 0)); EXPECT_CALL(visitor, OnWindowUpdate(0, 80000)); EXPECT_CALL(visitor, OnFrameHeader(1, 4, WINDOW_UPDATE, 0)); EXPECT_CALL(visitor, OnWindowUpdate(1, 20000)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), static_cast(stream_result)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id2, _, 0x0, 0)) .Times(testing::AtLeast(1)); EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id1, _, 0x0, 0)) .Times(testing::AtLeast(1)); EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); } TEST(OgHttp2AdapterTest, ClientSendsTrailersAfterFlowControlBlock) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
headers1 = ToHeaders({{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}); auto body1 = absl::make_unique(visitor, false); body1->AppendPayload("Really small body."); body1->EndData(); const int32_t stream_id1 = adapter->SubmitRequest(headers1, std::move(body1), nullptr); ASSERT_GT(stream_id1, 0); const std::vector
headers2 = ToHeaders({{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/two"}}); const std::string kBody = std::string(100 * 1024, 'a'); auto body2 = absl::make_unique(visitor, false); body2->AppendPayload(kBody); body2->EndData(); const int32_t stream_id2 = adapter->SubmitRequest(headers2, std::move(body2), nullptr); ASSERT_GT(stream_id2, 0); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x4)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x4, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id2, _, 0x4)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id2, _, 0x4, 0)); EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id1, _, 0x0, 0)).Times(1); // 4 DATA frames should saturate the default 64kB stream/connection flow // control window. EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id2, _, 0x0, 0)).Times(4); int result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_FALSE(adapter->want_write()); EXPECT_EQ(0, adapter->GetSendWindowSize()); const std::vector
trailers1 = ToHeaders({{"extra-info", "Trailers are weird but good?"}}); adapter->SubmitTrailer(stream_id1, trailers1); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0)); result = adapter->Send(); EXPECT_EQ(0, result); } TEST(OgHttp2AdapterTest, ClientSendsMetadataAfterFlowControlBlock) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::vector
headers1 = ToHeaders({{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}); const std::string kBody = std::string(100 * 1024, 'a'); auto body1 = absl::make_unique(visitor, false); body1->AppendPayload(kBody); body1->EndData(); const int32_t stream_id1 = adapter->SubmitRequest(headers1, std::move(body1), nullptr); ASSERT_GT(stream_id1, 0); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x4)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x4, 0)); // 4 DATA frames should saturate the default 64kB stream/connection flow // control window. EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id1, _, 0x0, 0)).Times(4); int result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_FALSE(adapter->want_write()); EXPECT_EQ(0, adapter->GetSendWindowSize()); auto source = absl::make_unique(ToHeaderBlock(ToHeaders( {{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}}))); adapter->SubmitMetadata(1, 16384u, std::move(source)); EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x4)); EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x4, 0)); result = adapter->Send(); EXPECT_EQ(0, result); } TEST(OgHttp2AdapterTest, SubmitMetadata) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); auto source = absl::make_unique(ToHeaderBlock(ToHeaders( {{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}}))); adapter->SubmitMetadata(1, 16384u, std::move(source)); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x4)); EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x4, 0)); int result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, static_cast(kMetadataFrameType)})); EXPECT_FALSE(adapter->want_write()); } TEST(OgHttp2AdapterTest, SubmitMetadataMultipleFrames) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); const auto kLargeValue = std::string(63 * 1024, 'a'); auto source = absl::make_unique( ToHeaderBlock(ToHeaders({{"large-value", kLargeValue}}))); adapter->SubmitMetadata(1, 16384u, std::move(source)); EXPECT_TRUE(adapter->want_write()); testing::InSequence seq; EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x4)); EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x4, 0)); int result = adapter->Send(); EXPECT_EQ(0, result); absl::string_view serialized = visitor.data(); EXPECT_THAT(serialized, EqualsFrames({SpdyFrameType::SETTINGS, static_cast(kMetadataFrameType), static_cast(kMetadataFrameType), static_cast(kMetadataFrameType), static_cast(kMetadataFrameType)})); EXPECT_FALSE(adapter->want_write()); } TEST(OgHttp2AdapterTest, SubmitConnectionMetadata) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); auto source = absl::make_unique(ToHeaderBlock(ToHeaders( {{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}}))); adapter->SubmitMetadata(0, 16384u, std::move(source)); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 0, _, 0x4)); EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 0, _, 0x4, 0)); int result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, static_cast(kMetadataFrameType)})); EXPECT_FALSE(adapter->want_write()); } TEST(OgHttp2AdapterTest, GetSendWindowSize) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); const int peer_window = adapter->GetSendWindowSize(); EXPECT_EQ(peer_window, kInitialFlowControlWindowSize); } TEST(OgHttp2AdapterTest, MarkDataConsumedForStream) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); EXPECT_QUICHE_BUG(adapter->MarkDataConsumedForStream(1, 11), "Stream 1 not found"); } TEST(OgHttp2AdapterTest, TestSerialize) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); EXPECT_TRUE(adapter->want_read()); EXPECT_FALSE(adapter->want_write()); adapter->SubmitSettings( {{HEADER_TABLE_SIZE, 128}, {MAX_FRAME_SIZE, 128 << 10}}); EXPECT_TRUE(adapter->want_write()); const Http2StreamId accepted_stream = 3; const Http2StreamId rejected_stream = 7; adapter->SubmitPriorityForStream(accepted_stream, 1, 255, true); adapter->SubmitRst(rejected_stream, Http2ErrorCode::CANCEL); adapter->SubmitPing(42); adapter->SubmitGoAway(13, Http2ErrorCode::HTTP2_NO_ERROR, ""); adapter->SubmitWindowUpdate(accepted_stream, 127); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(PRIORITY, accepted_stream, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(PRIORITY, accepted_stream, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, rejected_stream, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, rejected_stream, _, 0x0, 0x8)); EXPECT_CALL(visitor, OnBeforeFrameSent(PING, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(PING, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(GOAWAY, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(WINDOW_UPDATE, accepted_stream, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(WINDOW_UPDATE, accepted_stream, _, 0x0, 0)); int result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT( visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::PRIORITY, SpdyFrameType::RST_STREAM, SpdyFrameType::PING, SpdyFrameType::GOAWAY, SpdyFrameType::WINDOW_UPDATE})); EXPECT_FALSE(adapter->want_write()); } TEST(OgHttp2AdapterTest, TestPartialSerialize) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); EXPECT_FALSE(adapter->want_write()); adapter->SubmitSettings( {{HEADER_TABLE_SIZE, 128}, {MAX_FRAME_SIZE, 128 << 10}}); adapter->SubmitGoAway(13, Http2ErrorCode::HTTP2_NO_ERROR, "And don't come back!"); adapter->SubmitPing(42); EXPECT_TRUE(adapter->want_write()); visitor.set_send_limit(20); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); int result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(GOAWAY, 0, _, 0x0, 0)); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(PING, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(PING, 0, _, 0x0, 0)); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_FALSE(adapter->want_write()); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::GOAWAY, SpdyFrameType::PING})); } TEST(OgHttp2AdapterTest, ConnectionErrorOnControlFrameSent) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, 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(read_result), frames.size()); EXPECT_TRUE(adapter->want_write()); // Server preface (SETTINGS) EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); // SETTINGS ack EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)) .WillOnce(testing::Return(-902)); EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kSendError)); int send_result = adapter->Send(); EXPECT_LT(send_result, 0); EXPECT_FALSE(adapter->want_write()); send_result = adapter->Send(); EXPECT_LT(send_result, 0); } TEST(OgHttp2AdapterTest, ConnectionErrorOnDataFrameSent) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); 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, 0x5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(4); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnEndStream(1)); const int64_t read_result = adapter->ProcessBytes(frames); EXPECT_EQ(static_cast(read_result), frames.size()); auto body = absl::make_unique(visitor, true); body->AppendPayload("Here is some data, which will lead to a fatal error"); TestDataFrameSource* body_ptr = body.get(); int submit_result = adapter->SubmitResponse( 1, ToHeaders({{":status", "200"}}), std::move(body)); ASSERT_EQ(0, submit_result); EXPECT_TRUE(adapter->want_write()); // Server preface (SETTINGS) EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); // SETTINGS ack EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); // Stream 1, with doomed DATA EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x4)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x4, 0)); EXPECT_CALL(visitor, OnFrameSent(DATA, 1, _, 0x0, 0)) .WillOnce(testing::Return(-902)); EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kSendError)); int send_result = adapter->Send(); EXPECT_LT(send_result, 0); body_ptr->AppendPayload("After the fatal error, data will be sent no more"); EXPECT_FALSE(adapter->want_write()); send_result = adapter->Send(); EXPECT_LT(send_result, 0); } TEST(OgHttp2AdapterTest, ClientSendsContinuation) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); EXPECT_FALSE(adapter->want_write()); const std::string frames = TestFrameSequence() .ClientPreface() .Headers(1, {{":method", "GET"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/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(), static_cast(result)); } TEST(OgHttp2AdapterTest, ClientSendsMetadataWithContinuation) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); EXPECT_FALSE(adapter->want_write()); const std::string frames = TestFrameSequence() .ClientPreface() .Metadata(0, "Example connection metadata in multiple frames", true) .Headers(1, {{":method", "GET"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}, /*fin=*/false, /*add_continuation=*/true) .Metadata(1, "Some stream metadata that's also sent in multiple frames", true) .Serialize(); testing::InSequence s; // Client preface (empty SETTINGS) EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); EXPECT_CALL(visitor, OnSettingsStart()); EXPECT_CALL(visitor, OnSettingsEnd()); // Metadata on stream 0 EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 0)); EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _)); EXPECT_CALL(visitor, OnMetadataForStream(0, _)); EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 4)); EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _)); EXPECT_CALL(visitor, OnMetadataForStream(0, _)); EXPECT_CALL(visitor, OnMetadataEndForStream(0)); // Stream 1 EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 0)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "GET")); EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https")); EXPECT_CALL(visitor, OnFrameHeader(1, _, CONTINUATION, 4)); EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com")); EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one")); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); // Metadata on stream 1 EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 0)); EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _)); EXPECT_CALL(visitor, OnMetadataForStream(1, _)); EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 4)); EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _)); EXPECT_CALL(visitor, OnMetadataForStream(1, _)); EXPECT_CALL(visitor, OnMetadataEndForStream(1)); const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(frames.size(), static_cast(result)); EXPECT_EQ("Example connection metadata in multiple frames", absl::StrJoin(visitor.GetMetadata(0), "")); EXPECT_EQ("Some stream metadata that's also sent in multiple frames", absl::StrJoin(visitor.GetMetadata(1), "")); } TEST(OgHttp2AdapterTest, RepeatedHeaderNames) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); EXPECT_FALSE(adapter->want_write()); const std::string frames = TestFrameSequence() .ClientPreface() .Headers(1, {{":method", "GET"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}, {"accept", "text/plain"}, {"accept", "text/html"}}, /*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, OnHeaderForStream(1, "accept", "text/plain")); EXPECT_CALL(visitor, OnHeaderForStream(1, "accept", "text/html")); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnEndStream(1)); int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(frames.size(), static_cast(result)); const std::vector
headers1 = ToHeaders( {{":status", "200"}, {"content-length", "10"}, {"content-length", "10"}}); auto body1 = absl::make_unique(visitor, true); body1->AppendPayload("perfection"); body1->EndData(); int submit_result = adapter->SubmitResponse(1, headers1, std::move(body1)); ASSERT_EQ(0, submit_result); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); 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, 10, 0x1, 0)); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); int send_result = adapter->Send(); EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS, SpdyFrameType::DATA})); } TEST(OgHttp2AdapterTest, ServerRespondsToRequestWithTrailers) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); EXPECT_FALSE(adapter->want_write()); const std::string frames = TestFrameSequence() .ClientPreface() .Headers(1, {{":method", "GET"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}) .Data(1, "Example data, woohoo.") .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, 4)); 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, OnFrameHeader(1, _, DATA, 0)); EXPECT_CALL(visitor, OnBeginDataForStream(1, _)); EXPECT_CALL(visitor, OnDataForStream(1, _)); int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(frames.size(), static_cast(result)); const std::vector
headers1 = ToHeaders({{":status", "200"}}); auto body1 = absl::make_unique(visitor, true); TestDataFrameSource* body1_ptr = body1.get(); int submit_result = adapter->SubmitResponse(1, headers1, std::move(body1)); ASSERT_EQ(0, submit_result); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); 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)); int send_result = adapter->Send(); EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS})); visitor.Clear(); const std::string more_frames = TestFrameSequence() .Headers(1, {{"extra-info", "Trailers are weird but good?"}}, /*fin=*/true) .Serialize(); EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, "extra-info", "Trailers are weird but good?")); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnEndStream(1)); result = adapter->ProcessBytes(more_frames); EXPECT_EQ(more_frames.size(), static_cast(result)); body1_ptr->EndData(); EXPECT_EQ(true, adapter->ResumeStream(1)); EXPECT_CALL(visitor, OnFrameSent(DATA, 1, 0, 0x1, 0)); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); send_result = adapter->Send(); EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::DATA})); } TEST(OgHttp2AdapterTest, ServerReceivesMoreHeaderBytesThanConfigured) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer, .max_header_list_bytes = 42}; auto adapter = OgHttp2Adapter::Create(visitor, options); EXPECT_FALSE(adapter->want_write()); const std::string frames = TestFrameSequence() .ClientPreface() .Headers(1, {{":method", "GET"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}, {"from-douglas-de-fermat", "I have discovered a truly marvelous answer to the life, " "the universe, and everything that the header setting is " "too narrow to contain."}}, /*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, 0x5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kParseError)); int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(static_cast(result), frames.size()); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(GOAWAY, 0, _, 0x0, static_cast(Http2ErrorCode::COMPRESSION_ERROR))); int send_result = adapter->Send(); // Some bytes should have been serialized. EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::GOAWAY})); } TEST(OgHttp2AdapterTest, ServerSubmitsResponseWithDataSourceError) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); EXPECT_FALSE(adapter->want_write()); const std::string frames = TestFrameSequence() .ClientPreface() .Headers(1, {{":method", "GET"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/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(result)); auto body1 = absl::make_unique(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, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); 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)); // TODO(birenroy): Send RST_STREAM INTERNAL_ERROR to the client as well. EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::INTERNAL_ERROR)); int send_result = adapter->Send(); EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS})); visitor.Clear(); EXPECT_FALSE(adapter->want_write()); // Since the stream has been closed, it is not possible to submit trailers for // the stream. int trailer_result = adapter->SubmitTrailer(1, ToHeaders({{":final-status", "a-ok"}})); ASSERT_LT(trailer_result, 0); EXPECT_FALSE(adapter->want_write()); } TEST(OgHttp2AdapterTest, CompleteRequestWithServerResponse) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); EXPECT_FALSE(adapter->want_write()); const std::string frames = TestFrameSequence() .ClientPreface() .Headers(1, {{":method", "GET"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}, /*fin=*/false) .Data(1, "This is the response body.", /*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, 4)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(4); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnFrameHeader(1, _, DATA, 1)); EXPECT_CALL(visitor, OnBeginDataForStream(1, _)); EXPECT_CALL(visitor, OnDataForStream(1, _)); EXPECT_CALL(visitor, OnEndStream(1)); const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(frames.size(), static_cast(result)); int submit_result = adapter->SubmitResponse(1, ToHeaders({{":status", "200"}}), nullptr); EXPECT_EQ(submit_result, 0); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x5)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x5, 0)); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); int send_result = adapter->Send(); EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS})); EXPECT_FALSE(adapter->want_write()); } TEST(OgHttp2AdapterTest, IncompleteRequestWithServerResponse) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); EXPECT_FALSE(adapter->want_write()); const std::string frames = TestFrameSequence() .ClientPreface() .Headers(1, {{":method", "GET"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/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()); // Stream 1 EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(4); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(frames.size(), static_cast(result)); int submit_result = adapter->SubmitResponse(1, ToHeaders({{":status", "200"}}), nullptr); EXPECT_EQ(submit_result, 0); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x5)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x5, 0)); // RST_STREAM NO_ERROR option is disabled. int send_result = adapter->Send(); EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS})); EXPECT_FALSE(adapter->want_write()); } TEST(OgHttp2AdapterTest, IncompleteRequestWithServerResponseRstStreamEnabled) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer, .rst_stream_no_error_when_incomplete = true}; auto adapter = OgHttp2Adapter::Create(visitor, options); EXPECT_FALSE(adapter->want_write()); const std::string frames = TestFrameSequence() .ClientPreface() .Headers(1, {{":method", "GET"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/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()); // Stream 1 EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(4); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(frames.size(), static_cast(result)); int submit_result = adapter->SubmitResponse(1, ToHeaders({{":status", "200"}}), nullptr); EXPECT_EQ(submit_result, 0); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x5)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x5, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, 4, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 1, 4, 0x0, 0)); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); int send_result = adapter->Send(); EXPECT_EQ(0, send_result); EXPECT_THAT( visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS, SpdyFrameType::RST_STREAM})); EXPECT_FALSE(adapter->want_write()); } TEST(OgHttp2AdapterTest, ServerSendsInvalidTrailers) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); EXPECT_FALSE(adapter->want_write()); const std::string frames = TestFrameSequence() .ClientPreface() .Headers(1, {{":method", "GET"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/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(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(visitor, false); body1->AppendPayload(kBody); body1->EndData(); int submit_result = adapter->SubmitResponse( 1, ToHeaders({{":status", "200"}, {"x-comment", "Sure, sounds good."}}), std::move(body1)); EXPECT_EQ(submit_result, 0); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 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({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS, 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::HTTP2_NO_ERROR)); send_result = adapter->Send(); EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::HEADERS})); } TEST(OgHttp2AdapterTest, ServerHandlesDataWithPadding) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); 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.", /*fin=*/true, /*padding_length=*/39) .Headers(3, {{":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(1, _, HEADERS, 4)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(4); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnFrameHeader(1, 25 + 39, DATA, 0x9)); EXPECT_CALL(visitor, OnBeginDataForStream(1, 25 + 39)); // Note: oghttp2 passes padding information before the actual data. EXPECT_CALL(visitor, OnDataPaddingLength(1, 39)); EXPECT_CALL(visitor, OnDataForStream(1, "This is the request body.")); EXPECT_CALL(visitor, OnEndStream(1)); EXPECT_CALL(visitor, OnFrameHeader(3, _, HEADERS, 5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(3)); EXPECT_CALL(visitor, OnHeaderForStream(3, _, _)).Times(4); EXPECT_CALL(visitor, OnEndHeadersForStream(3)); EXPECT_CALL(visitor, OnEndStream(3)); const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(static_cast(frames.size()), result); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 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({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS})); } // Tests the case where the response body is in the progress of being sent while // trailers are queued. TEST(OgHttp2AdapterTest, ServerSubmitsTrailersWhileDataDeferred) { DataSavingVisitor visitor; for (const bool queue_trailers : {true, false}) { OgHttp2Adapter::Options options{ .perspective = Perspective::kServer, .trailers_require_end_data = queue_trailers}; auto adapter = OgHttp2Adapter::Create(visitor, options); 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, _, _)).Times(4); 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(result)); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); int send_result = adapter->Send(); EXPECT_EQ(0, send_result); 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(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(); EXPECT_EQ(0, send_result); visitor.Clear(); EXPECT_FALSE(adapter->want_write()); int trailer_result = adapter->SubmitTrailer(1, ToHeaders({{"final-status", "a-ok"}})); ASSERT_EQ(trailer_result, 0); if (queue_trailers) { // Even though there are new trailers to write, the data source has not // finished writing data and is blocked. EXPECT_FALSE(adapter->want_write()); body1_ptr->EndData(); adapter->ResumeStream(1); 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); } else { // Even though the data source has not finished sending data, the library // 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({SpdyFrameType::HEADERS})); } } } TEST(OgHttp2AdapterTest, ClientDisobeysConnectionFlowControl) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); 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) // 70000 bytes of data .Data(1, std::string(16384, 'a')) .Data(1, std::string(16384, 'a')) .Data(1, std::string(16384, 'a')) .Data(1, std::string(16384, 'a')) .Data(1, std::string(4464, 'a')) .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).Times(5); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); EXPECT_CALL(visitor, OnDataForStream(1, _)); EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); EXPECT_CALL(visitor, OnDataForStream(1, _)); EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); EXPECT_CALL(visitor, OnDataForStream(1, _)); EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); EXPECT_CALL(visitor, OnDataForStream(1, _)); EXPECT_CALL(visitor, OnFrameHeader(1, 4464, DATA, 0x0)); EXPECT_CALL(visitor, OnBeginDataForStream(1, 4464)); EXPECT_CALL(visitor, OnDataForStream(1, _)); const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(frames.size(), static_cast(result)); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 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({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS})); } TEST(OgHttp2AdapterTest, ClientDisobeysStreamFlowControl) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); 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) .Serialize(); const std::string more_frames = TestFrameSequence() // 70000 bytes of data .Data(1, std::string(16384, 'a')) .Data(1, std::string(16384, 'a')) .Data(1, std::string(16384, 'a')) .Data(1, std::string(16384, 'a')) .Data(1, std::string(4464, 'a')) .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).Times(5); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(frames.size(), static_cast(result)); adapter->SubmitWindowUpdate(0, 20000); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(WINDOW_UPDATE, 0, 4, 0x0)); EXPECT_CALL(visitor, OnFrameSent(WINDOW_UPDATE, 0, 4, 0x0, 0)); int send_result = adapter->Send(); EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, SpdyFrameType::WINDOW_UPDATE})); EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); EXPECT_CALL(visitor, OnDataForStream(1, _)); EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); EXPECT_CALL(visitor, OnDataForStream(1, _)); EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); EXPECT_CALL(visitor, OnDataForStream(1, _)); EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); EXPECT_CALL(visitor, OnDataForStream(1, _)); EXPECT_CALL(visitor, OnFrameHeader(1, 4464, DATA, 0x0)); EXPECT_CALL(visitor, OnBeginDataForStream(1, 4464)); EXPECT_CALL(visitor, OnDataForStream(1, _)); result = adapter->ProcessBytes(more_frames); EXPECT_EQ(more_frames.size(), static_cast(result)); EXPECT_FALSE(adapter->want_write()); } TEST(OgHttp2AdapterTest, ServerErrorWhileHandlingHeaders) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); 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)); // Stream WINDOW_UPDATE and DATA frames are 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(), static_cast(result)); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 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(Http2ErrorCode::INTERNAL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); int send_result = adapter->Send(); // Some bytes should have been serialized. EXPECT_EQ(0, send_result); // SETTINGS ack EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, SpdyFrameType::RST_STREAM})); } TEST(OgHttp2AdapterTest, ServerErrorWhileHandlingHeadersDropsFrames) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); 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.") .Metadata(1, "This is the request metadata.") .RstStream(1, Http2ErrorCode::CANCEL) .WindowUpdate(0, 2000) .Headers(3, {{":method", "GET"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/this/is/request/two"}}, /*fin=*/false) .Metadata(3, "This is the request metadata.", /*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()); EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(4); EXPECT_CALL(visitor, OnHeaderForStream(1, "accept", "some bogus value!")) .WillOnce(testing::Return(Http2VisitorInterface::HEADER_RST_STREAM)); // Frames for the RST_STREAM-marked stream are not delivered to the visitor. // Note: nghttp2 still delivers control frames and metadata for the stream. EXPECT_CALL(visitor, OnFrameHeader(0, 4, WINDOW_UPDATE, 0)); EXPECT_CALL(visitor, OnWindowUpdate(0, 2000)); EXPECT_CALL(visitor, OnFrameHeader(3, _, HEADERS, 4)); EXPECT_CALL(visitor, OnBeginHeadersForStream(3)); EXPECT_CALL(visitor, OnHeaderForStream(3, _, _)).Times(4); EXPECT_CALL(visitor, OnEndHeadersForStream(3)); EXPECT_CALL(visitor, OnFrameHeader(3, _, kMetadataFrameType, 0)); EXPECT_CALL(visitor, OnBeginMetadataForStream(3, _)); EXPECT_CALL(visitor, OnMetadataForStream(3, "This is the re")) .WillOnce(testing::DoAll(testing::InvokeWithoutArgs([&adapter]() { adapter->SubmitRst( 3, Http2ErrorCode::REFUSED_STREAM); }), testing::Return(true))); // The rest of the metadata is not delivered to the visitor. const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(frames.size(), static_cast(result)); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 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(Http2ErrorCode::INTERNAL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 3, 4, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 3, 4, 0x0, static_cast(Http2ErrorCode::REFUSED_STREAM))); EXPECT_CALL(visitor, OnCloseStream(3, Http2ErrorCode::HTTP2_NO_ERROR)); int send_result = adapter->Send(); // Some bytes should have been serialized. EXPECT_EQ(0, send_result); // SETTINGS ack EXPECT_THAT( visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, SpdyFrameType::RST_STREAM, SpdyFrameType::RST_STREAM})); } TEST(OgHttp2AdapterTest, ServerConnectionErrorWhileHandlingHeaders) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); 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, OnInvalidFrame(1, Http2VisitorInterface::InvalidFrameError::kHttpHeader)) .WillOnce(testing::Return(false)); EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kHeaderError)); const int64_t result = adapter->ProcessBytes(frames); EXPECT_LT(result, 0); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, 4, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 1, 4, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(GOAWAY, 0, _, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); int send_result = adapter->Send(); // Some bytes should have been serialized. EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::RST_STREAM, SpdyFrameType::GOAWAY})); } TEST(OgHttp2AdapterTest, ServerErrorAfterHandlingHeaders) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); 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_LT(result, 0); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(GOAWAY, 0, _, 0x0, static_cast(Http2ErrorCode::INTERNAL_ERROR))); int send_result = adapter->Send(); // Some bytes should have been serialized. EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::GOAWAY})); } // 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(OgHttp2AdapterTest, ServerRejectsFrameHeader) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); 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_LT(result, 0); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(GOAWAY, 0, _, 0x0, static_cast(Http2ErrorCode::INTERNAL_ERROR))); int send_result = adapter->Send(); // Some bytes should have been serialized. EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::GOAWAY})); } TEST(OgHttp2AdapterTest, ServerRejectsBeginningOfData) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); 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_LT(result, 0); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(GOAWAY, 0, _, 0x0, static_cast(Http2ErrorCode::INTERNAL_ERROR))); int send_result = adapter->Send(); // Some bytes should have been serialized. EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::GOAWAY})); } TEST(OgHttp2AdapterTest, ServerReceivesTooLargeHeader) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer, .max_header_list_bytes = 64 * 1024, .max_header_field_size = 64 * 1024}; auto adapter = OgHttp2Adapter::Create(visitor, options); // Due to configuration, the library will accept a maximum of 64kB of huffman // encoded data per header field. const std::string too_large_value = std::string(80 * 1024, 'q'); const std::string frames = TestFrameSequence() .ClientPreface() .Headers(1, {{":method", "POST"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}, {"x-toobig", too_large_value}}, /*fin=*/true) .Headers(3, {{":method", "GET"}, {":scheme", "https"}, {":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(1, _, HEADERS, END_STREAM)); 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, OnFrameHeader(1, _, CONTINUATION, 0)).Times(3); EXPECT_CALL(visitor, OnFrameHeader(1, _, CONTINUATION, END_HEADERS)); // Further header processing is skipped, as the header field is too large. EXPECT_CALL(visitor, OnFrameHeader(3, _, HEADERS, END_STREAM | END_HEADERS)); EXPECT_CALL(visitor, OnBeginHeadersForStream(3)); EXPECT_CALL(visitor, OnHeaderForStream(3, _, _)).Times(4); EXPECT_CALL(visitor, OnEndHeadersForStream(3)); EXPECT_CALL(visitor, OnEndStream(3)); const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(static_cast(frames.size()), result); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, ACK)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, ACK, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, 4, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 1, 4, 0x0, static_cast(Http2ErrorCode::INTERNAL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); int send_result = adapter->Send(); EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, SpdyFrameType::RST_STREAM})); } TEST(OgHttp2AdapterTest, ServerReceivesInvalidAuthority) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); const std::string frames = TestFrameSequence() .ClientPreface() .Headers(1, {{":method", "POST"}, {":scheme", "https"}, {":authority", "ex|ample.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, OnInvalidFrame(1, Http2VisitorInterface::InvalidFrameError::kHttpHeader)); const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(static_cast(frames.size()), result); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0x0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0x0)); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, 4, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 1, 4, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); int send_result = adapter->Send(); EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, SpdyFrameType::RST_STREAM})); } TEST(OgHttp2AdapterTest, ServerRejectsStreamData) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); 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_LT(result, 0); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(GOAWAY, 0, _, 0x0, static_cast(Http2ErrorCode::INTERNAL_ERROR))); int send_result = adapter->Send(); // Some bytes should have been serialized. EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::GOAWAY})); } // Exercises a naive mutually recursive test client and server. This test fails // without recursion guards in OgHttp2Session. TEST(OgHttp2AdapterInteractionTest, ClientServerInteractionTest) { MockHttp2Visitor client_visitor; auto client_adapter = OgHttp2Adapter::Create(client_visitor, {Perspective::kClient}); MockHttp2Visitor server_visitor; auto server_adapter = OgHttp2Adapter::Create(server_visitor, {Perspective::kServer}); // Feeds bytes sent from the client into the server's ProcessBytes. EXPECT_CALL(client_visitor, OnReadyToSend(_)) .WillRepeatedly( testing::Invoke(server_adapter.get(), &OgHttp2Adapter::ProcessBytes)); // Feeds bytes sent from the server into the client's ProcessBytes. EXPECT_CALL(server_visitor, OnReadyToSend(_)) .WillRepeatedly( testing::Invoke(client_adapter.get(), &OgHttp2Adapter::ProcessBytes)); // Sets up the server to respond automatically to a request from a client. EXPECT_CALL(server_visitor, OnEndHeadersForStream(_)) .WillRepeatedly([&server_adapter](Http2StreamId stream_id) { server_adapter->SubmitResponse( stream_id, ToHeaders({{":status", "200"}}), nullptr); server_adapter->Send(); return true; }); // Sets up the client to create a new stream automatically when receiving a // response. EXPECT_CALL(client_visitor, OnEndHeadersForStream(_)) .WillRepeatedly([&client_adapter, &client_visitor](Http2StreamId stream_id) { if (stream_id < 10) { const Http2StreamId new_stream_id = stream_id + 2; auto body = absl::make_unique(client_visitor, true); body->AppendPayload("This is an example request body."); body->EndData(); const int created_stream_id = client_adapter->SubmitRequest( ToHeaders({{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", absl::StrCat("/this/is/request/", new_stream_id)}}), std::move(body), nullptr); EXPECT_EQ(new_stream_id, created_stream_id); client_adapter->Send(); } return true; }); // Submit a request to ensure the first stream is created. int stream_id = client_adapter->SubmitRequest( ToHeaders({{":method", "POST"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}), nullptr, nullptr); EXPECT_EQ(stream_id, 1); client_adapter->Send(); } TEST(OgHttp2AdapterInteractionTest, ClientServerInteractionRepeatedHeaderNames) { DataSavingVisitor client_visitor; OgHttp2Adapter::Options client_options{.perspective = Perspective::kClient}; auto client_adapter = OgHttp2Adapter::Create(client_visitor, client_options); const std::vector
headers1 = ToHeaders({{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}, {"accept", "text/plain"}, {"accept", "text/html"}}); const int32_t stream_id1 = client_adapter->SubmitRequest(headers1, nullptr, nullptr); ASSERT_GT(stream_id1, 0); EXPECT_CALL(client_visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(client_visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(client_visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5)); EXPECT_CALL(client_visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0)); int send_result = client_adapter->Send(); EXPECT_EQ(0, send_result); DataSavingVisitor server_visitor; OgHttp2Adapter::Options server_options{.perspective = Perspective::kServer}; auto server_adapter = OgHttp2Adapter::Create(server_visitor, server_options); testing::InSequence s; // Client preface (empty SETTINGS) EXPECT_CALL(server_visitor, OnFrameHeader(0, _, SETTINGS, 0)); EXPECT_CALL(server_visitor, OnSettingsStart()); EXPECT_CALL(server_visitor, OnSetting(_)).Times(testing::AnyNumber()); EXPECT_CALL(server_visitor, OnSettingsEnd()); // Stream 1 EXPECT_CALL(server_visitor, OnFrameHeader(1, _, HEADERS, 5)); EXPECT_CALL(server_visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(server_visitor, OnHeaderForStream(1, ":method", "GET")); EXPECT_CALL(server_visitor, OnHeaderForStream(1, ":scheme", "http")); EXPECT_CALL(server_visitor, OnHeaderForStream(1, ":authority", "example.com")); EXPECT_CALL(server_visitor, OnHeaderForStream(1, ":path", "/this/is/request/one")); EXPECT_CALL(server_visitor, OnHeaderForStream(1, "accept", "text/plain")); EXPECT_CALL(server_visitor, OnHeaderForStream(1, "accept", "text/html")); EXPECT_CALL(server_visitor, OnEndHeadersForStream(1)); EXPECT_CALL(server_visitor, OnEndStream(1)); int64_t result = server_adapter->ProcessBytes(client_visitor.data()); EXPECT_EQ(client_visitor.data().size(), static_cast(result)); } TEST(OgHttp2AdapterTest, ServerForbidsNewStreamBelowWatermark) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); 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.")); EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5)); EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kInvalidNewStreamId)); const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(static_cast(result), frames.size()); EXPECT_EQ(3, adapter->GetHighestReceivedStreamId()); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(GOAWAY, 0, _, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); int send_result = adapter->Send(); // Some bytes should have been serialized. EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::GOAWAY})); } TEST(OgHttp2AdapterTest, ServerForbidsWindowUpdateOnIdleStream) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); 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, OnConnectionError(ConnectionError::kWrongFrameSequence)); const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(static_cast(result), frames.size()); EXPECT_EQ(1, adapter->GetHighestReceivedStreamId()); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(GOAWAY, 0, _, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); int send_result = adapter->Send(); // Some bytes should have been serialized. EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::GOAWAY})); } TEST(OgHttp2AdapterTest, ServerForbidsDataOnIdleStream) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); 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()); EXPECT_CALL(visitor, OnFrameHeader(1, _, DATA, 0)); EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kWrongFrameSequence)); const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(static_cast(result), frames.size()); EXPECT_EQ(1, adapter->GetHighestReceivedStreamId()); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(GOAWAY, 0, _, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); int send_result = adapter->Send(); // Some bytes should have been serialized. EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::GOAWAY})); } TEST(OgHttp2AdapterTest, ServerForbidsRstStreamOnIdleStream) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); 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, OnConnectionError(ConnectionError::kWrongFrameSequence)); const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(static_cast(result), frames.size()); EXPECT_EQ(1, adapter->GetHighestReceivedStreamId()); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(GOAWAY, 0, _, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); int send_result = adapter->Send(); // Some bytes should have been serialized. EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::GOAWAY})); } TEST(OgHttp2AdapterTest, ServerForbidsNewStreamAboveStreamLimit) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); adapter->SubmitSettings({{MAX_CONCURRENT_STREAMS, 1}}); const std::string initial_frames = TestFrameSequence().ClientPreface().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 initial_result = adapter->ProcessBytes(initial_frames); EXPECT_EQ(static_cast(initial_result), initial_frames.size()); EXPECT_TRUE(adapter->want_write()); // Server initial SETTINGS (with MAX_CONCURRENT_STREAMS) and SETTINGS ack. EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 6, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 6, 0x0, 0)); 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({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS})); visitor.Clear(); // Let the client send a SETTINGS ack and then attempt to open more than the // advertised number of streams. The overflow stream should be rejected. const std::string stream_frames = TestFrameSequence() .SettingsAck() .Headers(1, {{":method", "GET"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}, /*fin=*/true) .Headers(3, {{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/two"}}, /*fin=*/true) .Serialize(); EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0x1)); EXPECT_CALL(visitor, OnSettingsAck()); EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 0x5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(4); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnEndStream(1)); EXPECT_CALL(visitor, OnFrameHeader(3, _, HEADERS, 0x5)); EXPECT_CALL( visitor, OnInvalidFrame(3, Http2VisitorInterface::InvalidFrameError::kProtocol)); // The oghttp2 stack also signals the error via OnConnectionError(). EXPECT_CALL(visitor, OnConnectionError(Http2VisitorInterface::ConnectionError:: kExceededMaxConcurrentStreams)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(static_cast(stream_result), stream_frames.size()); // The server should send a GOAWAY for this error, even though // OnInvalidFrame() returns true. EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(GOAWAY, 0, _, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); send_result = adapter->Send(); EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::GOAWAY})); } TEST(OgHttp2AdapterTest, ServerRstStreamsNewStreamAboveStreamLimitBeforeAck) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); adapter->SubmitSettings({{MAX_CONCURRENT_STREAMS, 1}}); const std::string initial_frames = TestFrameSequence().ClientPreface().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 initial_result = adapter->ProcessBytes(initial_frames); EXPECT_EQ(static_cast(initial_result), initial_frames.size()); EXPECT_TRUE(adapter->want_write()); // Server initial SETTINGS (with MAX_CONCURRENT_STREAMS) and SETTINGS ack. EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 6, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 6, 0x0, 0)); 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({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS})); visitor.Clear(); // Let the client avoid sending a SETTINGS ack and attempt to open more than // the advertised number of streams. The server should still reject the // overflow stream, albeit with RST_STREAM REFUSED_STREAM instead of GOAWAY. const std::string stream_frames = TestFrameSequence() .Headers(1, {{":method", "GET"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}}, /*fin=*/true) .Headers(3, {{":method", "GET"}, {":scheme", "http"}, {":authority", "example.com"}, {":path", "/this/is/request/two"}}, /*fin=*/true) .Serialize(); EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 0x5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(4); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnEndStream(1)); EXPECT_CALL(visitor, OnFrameHeader(3, _, HEADERS, 0x5)); EXPECT_CALL(visitor, OnInvalidFrame( 3, Http2VisitorInterface::InvalidFrameError::kRefusedStream)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(static_cast(stream_result), stream_frames.size()); // The server sends a RST_STREAM for the offending stream. EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 3, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 3, _, 0x0, static_cast(Http2ErrorCode::REFUSED_STREAM))); send_result = adapter->Send(); EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::RST_STREAM})); } TEST(OgHttp2AdapterTest, ServerForbidsProtocolPseudoheaderBeforeAck) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer, .allow_extended_connect = false}; auto adapter = OgHttp2Adapter::Create(visitor, options); const std::string initial_frames = TestFrameSequence().ClientPreface().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 initial_result = adapter->ProcessBytes(initial_frames); EXPECT_EQ(static_cast(initial_result), initial_frames.size()); // The client attempts to send a CONNECT request with the `:protocol` // pseudoheader before receiving the server's SETTINGS frame. const std::string stream1_frames = TestFrameSequence() .Headers(1, {{":method", "CONNECT"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}, {":protocol", "websocket"}}, /*fin=*/true) .Serialize(); EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 0x5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(5); EXPECT_CALL(visitor, OnInvalidFrame( 1, Http2VisitorInterface::InvalidFrameError::kHttpMessaging)); int64_t stream_result = adapter->ProcessBytes(stream1_frames); EXPECT_EQ(static_cast(stream_result), stream1_frames.size()); // Server initial SETTINGS and SETTINGS ack. EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); // The server sends a RST_STREAM for the offending stream. EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 1, _, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); // Server settings with ENABLE_CONNECT_PROTOCOL. EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 6, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 6, 0x0, 0)); adapter->SubmitSettings({{ENABLE_CONNECT_PROTOCOL, 1}}); int send_result = adapter->Send(); EXPECT_EQ(0, send_result); EXPECT_THAT( visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, SpdyFrameType::RST_STREAM, SpdyFrameType::SETTINGS})); visitor.Clear(); // The client attempts to send a CONNECT request with the `:protocol` // pseudoheader before acking the server's SETTINGS frame. const std::string stream3_frames = TestFrameSequence() .Headers(3, {{":method", "CONNECT"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/this/is/request/two"}, {":protocol", "websocket"}}, /*fin=*/true) .Serialize(); // After sending SETTINGS with `ENABLE_CONNECT_PROTOCOL`, oghttp2 matches // nghttp2 in allowing this, even though the `allow_extended_connect` option // is false. EXPECT_CALL(visitor, OnFrameHeader(3, _, HEADERS, 0x5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(3)); EXPECT_CALL(visitor, OnHeaderForStream(3, _, _)).Times(5); EXPECT_CALL(visitor, OnEndHeadersForStream(3)); EXPECT_CALL(visitor, OnEndStream(3)); stream_result = adapter->ProcessBytes(stream3_frames); EXPECT_EQ(static_cast(stream_result), stream3_frames.size()); EXPECT_FALSE(adapter->want_write()); } TEST(OgHttp2AdapterTest, ServerAllowsProtocolPseudoheaderAfterAck) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); adapter->SubmitSettings({{ENABLE_CONNECT_PROTOCOL, 1}}); const std::string initial_frames = TestFrameSequence().ClientPreface().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 initial_result = adapter->ProcessBytes(initial_frames); EXPECT_EQ(static_cast(initial_result), initial_frames.size()); // Server initial SETTINGS (with ENABLE_CONNECT_PROTOCOL) and SETTINGS ack. EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 6, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 6, 0x0, 0)); 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); visitor.Clear(); // The client attempts to send a CONNECT request with the `:protocol` // pseudoheader after acking the server's SETTINGS frame. const std::string stream_frames = TestFrameSequence() .SettingsAck() .Headers(1, {{":method", "CONNECT"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}, {":protocol", "websocket"}}, /*fin=*/true) .Serialize(); EXPECT_CALL(visitor, OnFrameHeader(0, _, SETTINGS, 0x1)); EXPECT_CALL(visitor, OnSettingsAck()); EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 0x5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(5); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnEndStream(1)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(static_cast(stream_result), stream_frames.size()); EXPECT_FALSE(adapter->want_write()); } TEST(OgHttp2AdapterTest, SkipsSendingFramesForRejectedStream) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); const std::string initial_frames = TestFrameSequence() .ClientPreface() .Headers(1, {{":method", "GET"}, {":scheme", "http"}, {":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()); EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 0x5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(4); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnEndStream(1)); const int64_t initial_result = adapter->ProcessBytes(initial_frames); EXPECT_EQ(static_cast(initial_result), initial_frames.size()); auto body = absl::make_unique(visitor, true); body->AppendPayload("Here is some data, which will be completely ignored!"); int submit_result = adapter->SubmitResponse( 1, ToHeaders({{":status", "200"}}), std::move(body)); ASSERT_EQ(0, submit_result); auto source = absl::make_unique(ToHeaderBlock(ToHeaders( {{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}}))); adapter->SubmitMetadata(1, 16384u, std::move(source)); adapter->SubmitWindowUpdate(1, 1024); adapter->SubmitRst(1, Http2ErrorCode::INTERNAL_ERROR); // Server initial SETTINGS and SETTINGS ack. EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); // The server sends a RST_STREAM for the offending stream. // The response HEADERS, DATA and WINDOW_UPDATE are all ignored. EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 1, _, 0x0, static_cast(Http2ErrorCode::INTERNAL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); int send_result = adapter->Send(); EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, SpdyFrameType::RST_STREAM})); } TEST(OgHttpAdapterServerTest, ServerStartsShutdown) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); EXPECT_FALSE(adapter->want_write()); adapter->SubmitShutdownNotice(); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(GOAWAY, 0, _, 0x0, 0)); int result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::GOAWAY})); } TEST(OgHttp2AdapterTest, ServerStartsShutdownAfterGoaway) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); EXPECT_FALSE(adapter->want_write()); adapter->SubmitGoAway(1, Http2ErrorCode::HTTP2_NO_ERROR, "and don't come back!"); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(GOAWAY, 0, _, 0x0, 0)); int result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::GOAWAY})); // No-op, since a GOAWAY has previously been enqueued. adapter->SubmitShutdownNotice(); EXPECT_FALSE(adapter->want_write()); } // Verifies that a connection-level processing error results in repeatedly // returning a positive value for ProcessBytes() to mark all data as consumed // when the blackhole option is enabled. TEST(OgHttp2AdapterTest, ConnectionErrorWithBlackholingData) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer, .blackhole_data_on_connection_error = true}; auto adapter = OgHttp2Adapter::Create(visitor, options); 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, OnConnectionError(ConnectionError::kWrongFrameSequence)); const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(static_cast(result), frames.size()); // Ask the connection to process more bytes. Because the option is enabled, // the data should be marked as consumed. const std::string next_frame = TestFrameSequence().Ping(42).Serialize(); const int64_t next_result = adapter->ProcessBytes(next_frame); EXPECT_EQ(static_cast(next_result), next_frame.size()); } // Verifies that a connection-level processing error results in returning a // negative value for ProcessBytes() when the blackhole option is disabled. TEST(OgHttp2AdapterTest, ConnectionErrorWithoutBlackholingData) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer, .blackhole_data_on_connection_error = false}; auto adapter = OgHttp2Adapter::Create(visitor, options); 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, OnConnectionError(ConnectionError::kWrongFrameSequence)); const int64_t result = adapter->ProcessBytes(frames); EXPECT_LT(result, 0); // Ask the connection to process more bytes. Because the option is disabled, // ProcessBytes() should continue to return an error. const std::string next_frame = TestFrameSequence().Ping(42).Serialize(); const int64_t next_result = adapter->ProcessBytes(next_frame); EXPECT_LT(next_result, 0); } TEST(OgHttp2AdapterTest, ServerDoesNotSendFramesAfterImmediateGoAway) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); // Submit a custom initial SETTINGS frame with one setting. adapter->SubmitSettings({{HEADER_TABLE_SIZE, 100u}}); 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, 0x5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(4); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnEndStream(1)); const int64_t read_result = adapter->ProcessBytes(frames); EXPECT_EQ(static_cast(read_result), frames.size()); // Submit a response for the stream. auto body = absl::make_unique(visitor, true); body->AppendPayload("This data is doomed to never be written."); int submit_result = adapter->SubmitResponse( 1, ToHeaders({{":status", "200"}}), std::move(body)); ASSERT_EQ(0, submit_result); // Submit a WINDOW_UPDATE frame. adapter->SubmitWindowUpdate(kConnectionStreamId, 42); // Submit another SETTINGS frame. adapter->SubmitSettings({}); // Submit some metadata. auto source = absl::make_unique(ToHeaderBlock(ToHeaders( {{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}}))); adapter->SubmitMetadata(1, 16384u, std::move(source)); EXPECT_TRUE(adapter->want_write()); // Trigger a connection error. Only the response headers will be written. const std::string connection_error_frames = TestFrameSequence().WindowUpdate(3, 42).Serialize(); EXPECT_CALL(visitor, OnFrameHeader(3, 4, WINDOW_UPDATE, 0)); EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kWrongFrameSequence)); const int64_t result = adapter->ProcessBytes(connection_error_frames); EXPECT_EQ(static_cast(result), connection_error_frames.size()); EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 6, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 6, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(GOAWAY, 0, _, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); int send_result = adapter->Send(); // Some bytes should have been serialized. EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::GOAWAY})); visitor.Clear(); // Try to submit more frames for writing. They should not be written. adapter->SubmitPing(42); // TODO(diannahu): Enable the below expectation. // EXPECT_FALSE(adapter->want_write()); send_result = adapter->Send(); EXPECT_EQ(0, send_result); EXPECT_THAT(visitor.data(), testing::IsEmpty()); } TEST(OgHttp2AdapterTest, ServerHandlesContentLength) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::string stream_frames = TestFrameSequence() .ClientPreface() .Headers(1, {{":method", "GET"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/this/is/request/one"}, {"content-length", "2"}}) .Data(1, "hi", /*fin=*/true) .Headers(3, {{":method", "GET"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/this/is/request/three"}, {"content-length", "nan"}}, /*fin=*/true) .Serialize(); // Client preface (empty SETTINGS) EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); EXPECT_CALL(visitor, OnSettingsStart()); EXPECT_CALL(visitor, OnSettingsEnd()); // Stream 1: content-length is correct EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(5); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnFrameHeader(1, _, DATA, 1)); EXPECT_CALL(visitor, OnBeginDataForStream(1, 2)); EXPECT_CALL(visitor, OnDataForStream(1, "hi")); EXPECT_CALL(visitor, OnEndStream(1)); // Stream 3: content-length is not a number EXPECT_CALL(visitor, OnFrameHeader(3, _, HEADERS, 5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(3)); EXPECT_CALL(visitor, OnHeaderForStream(3, _, _)).Times(4); EXPECT_CALL( visitor, OnInvalidFrame(3, Http2VisitorInterface::InvalidFrameError::kHttpHeader)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), static_cast(stream_result)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 3, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 3, _, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(3, Http2ErrorCode::HTTP2_NO_ERROR)); EXPECT_TRUE(adapter->want_write()); int result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, SpdyFrameType::RST_STREAM})); } TEST(OgHttp2AdapterTest, ServerHandlesContentLengthMismatch) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::string stream_frames = TestFrameSequence() .ClientPreface() .Headers(1, {{":method", "GET"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/this/is/request/two"}, {"content-length", "2"}}) .Data(1, "h", /*fin=*/true) .Headers(3, {{":method", "GET"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/this/is/request/three"}, {"content-length", "2"}}) .Data(3, "howdy", /*fin=*/true) .Headers(5, {{":method", "GET"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/this/is/request/four"}, {"content-length", "2"}}, /*fin=*/true) .Headers(7, {{":method", "GET"}, {":scheme", "https"}, {":authority", "example.com"}, {":path", "/this/is/request/four"}, {"content-length", "2"}}, /*fin=*/false) .Data(7, "h", /*fin=*/false) .Headers(7, {{"extra-info", "Trailers with content-length mismatch"}}, /*fin=*/true) .Serialize(); // Client preface (empty SETTINGS) EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); EXPECT_CALL(visitor, OnSettingsStart()); EXPECT_CALL(visitor, OnSettingsEnd()); // Stream 1: content-length is larger than actual data // All data is delivered to the visitor. Note that neither oghttp2 nor // nghttp2 delivers OnInvalidFrame(). EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(5); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnFrameHeader(1, _, DATA, 1)); EXPECT_CALL(visitor, OnBeginDataForStream(1, 1)); EXPECT_CALL(visitor, OnDataForStream(1, "h")); // Stream 3: content-length is smaller than actual data // The beginning of data is delivered to the visitor, but not the actual data. // Again, neither oghttp2 nor nghttp2 delivers OnInvalidFrame(). EXPECT_CALL(visitor, OnFrameHeader(3, _, HEADERS, 4)); EXPECT_CALL(visitor, OnBeginHeadersForStream(3)); EXPECT_CALL(visitor, OnHeaderForStream(3, _, _)).Times(5); EXPECT_CALL(visitor, OnEndHeadersForStream(3)); EXPECT_CALL(visitor, OnFrameHeader(3, _, DATA, 1)); EXPECT_CALL(visitor, OnBeginDataForStream(3, 5)); // Stream 5: content-length is invalid and HEADERS ends the stream // Only oghttp2 invokes OnEndHeadersForStream(). Only nghttp2 invokes // OnInvalidFrame(). EXPECT_CALL(visitor, OnFrameHeader(5, _, HEADERS, 5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(5)); EXPECT_CALL(visitor, OnHeaderForStream(5, _, _)).Times(5); EXPECT_CALL(visitor, OnEndHeadersForStream(5)); // Stream 7: content-length is invalid and trailers end the stream // Only oghttp2 invokes OnEndHeadersForStream(). Only nghttp2 invokes // OnInvalidFrame(). EXPECT_CALL(visitor, OnFrameHeader(7, _, HEADERS, 4)); EXPECT_CALL(visitor, OnBeginHeadersForStream(7)); EXPECT_CALL(visitor, OnHeaderForStream(7, _, _)).Times(5); EXPECT_CALL(visitor, OnEndHeadersForStream(7)); EXPECT_CALL(visitor, OnFrameHeader(7, _, DATA, 0)); EXPECT_CALL(visitor, OnBeginDataForStream(7, 1)); EXPECT_CALL(visitor, OnDataForStream(7, "h")); EXPECT_CALL(visitor, OnFrameHeader(7, _, HEADERS, 5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(7)); EXPECT_CALL(visitor, OnHeaderForStream(7, _, _)); EXPECT_CALL(visitor, OnEndHeadersForStream(7)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), static_cast(stream_result)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 1, _, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 3, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 3, _, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(3, Http2ErrorCode::HTTP2_NO_ERROR)); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 5, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 5, _, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(5, Http2ErrorCode::HTTP2_NO_ERROR)); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 7, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 7, _, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(7, Http2ErrorCode::HTTP2_NO_ERROR)); EXPECT_TRUE(adapter->want_write()); int result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT( visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, SpdyFrameType::RST_STREAM, SpdyFrameType::RST_STREAM, SpdyFrameType::RST_STREAM, SpdyFrameType::RST_STREAM})); } TEST(OgHttp2AdapterTest, ServerHandlesAsteriskPathForOptions) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::string stream_frames = TestFrameSequence() .ClientPreface() .Headers(1, {{":scheme", "https"}, {":authority", "example.com"}, {":path", "*"}, {":method", "OPTIONS"}}, /*fin=*/true) .Serialize(); // 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, 5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(4); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnEndStream(1)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), static_cast(stream_result)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); EXPECT_TRUE(adapter->want_write()); int result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS})); } TEST(OgHttp2AdapterTest, ServerHandlesInvalidPath) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::string stream_frames = TestFrameSequence() .ClientPreface() .Headers(1, {{":scheme", "https"}, {":authority", "example.com"}, {":path", "*"}, {":method", "GET"}}, /*fin=*/true) .Headers(3, {{":scheme", "https"}, {":authority", "example.com"}, {":path", "other/non/slash/starter"}, {":method", "GET"}}, /*fin=*/true) .Headers(5, {{":scheme", "https"}, {":authority", "example.com"}, {":path", ""}, {":method", "GET"}}, /*fin=*/true) .Serialize(); // 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, 5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(4); EXPECT_CALL(visitor, OnInvalidFrame( 1, Http2VisitorInterface::InvalidFrameError::kHttpMessaging)); EXPECT_CALL(visitor, OnFrameHeader(3, _, HEADERS, 5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(3)); EXPECT_CALL(visitor, OnHeaderForStream(3, _, _)).Times(4); EXPECT_CALL(visitor, OnInvalidFrame( 3, Http2VisitorInterface::InvalidFrameError::kHttpMessaging)); EXPECT_CALL(visitor, OnFrameHeader(5, _, HEADERS, 5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(5)); EXPECT_CALL(visitor, OnHeaderForStream(5, _, _)).Times(2); EXPECT_CALL( visitor, OnInvalidFrame(5, Http2VisitorInterface::InvalidFrameError::kHttpHeader)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), static_cast(stream_result)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 1, _, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 3, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 3, _, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(3, Http2ErrorCode::HTTP2_NO_ERROR)); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 5, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 5, _, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(5, Http2ErrorCode::HTTP2_NO_ERROR)); EXPECT_TRUE(adapter->want_write()); int result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT( visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, SpdyFrameType::RST_STREAM, SpdyFrameType::RST_STREAM, SpdyFrameType::RST_STREAM})); } TEST(OgHttp2AdapterTest, ServerHandlesTeHeader) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); testing::InSequence s; const std::string stream_frames = TestFrameSequence() .ClientPreface() .Headers(1, {{":scheme", "https"}, {":authority", "example.com"}, {":path", "/"}, {":method", "GET"}, {"te", "trailers"}}, /*fin=*/true) .Headers(3, {{":scheme", "https"}, {":authority", "example.com"}, {":path", "/"}, {":method", "GET"}, {"te", "trailers, deflate"}}, /*fin=*/true) .Serialize(); // Client preface (empty SETTINGS) EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); EXPECT_CALL(visitor, OnSettingsStart()); EXPECT_CALL(visitor, OnSettingsEnd()); // Stream 1: TE: trailers should be allowed. EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(5); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnEndStream(1)); // Stream 3: TE: should be rejected. EXPECT_CALL(visitor, OnFrameHeader(3, _, HEADERS, 5)); EXPECT_CALL(visitor, OnBeginHeadersForStream(3)); EXPECT_CALL(visitor, OnHeaderForStream(3, _, _)).Times(4); EXPECT_CALL( visitor, OnInvalidFrame(3, Http2VisitorInterface::InvalidFrameError::kHttpHeader)); const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), static_cast(stream_result)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 3, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 3, _, 0x0, static_cast(Http2ErrorCode::PROTOCOL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(3, Http2ErrorCode::HTTP2_NO_ERROR)); EXPECT_TRUE(adapter->want_write()); int result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, SpdyFrameType::RST_STREAM})); } } // namespace } // namespace test } // namespace adapter } // namespace http2