| #include "http2/adapter/oghttp2_adapter.h" |
| |
| #include <string> |
| |
| #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<size_t>(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<size_t>(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<size_t>(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<size_t>(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<size_t>(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<size_t>(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<Header> 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<size_t>(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, QueuingWindowUpdateAffectsWindow) { |
| DataSavingVisitor visitor; |
| OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; |
| auto adapter = OgHttp2Adapter::Create(visitor, options); |
| |
| EXPECT_EQ(adapter->GetReceiveWindowSize(), kInitialFlowControlWindowSize); |
| adapter->SubmitWindowUpdate(0, 10000); |
| EXPECT_EQ(adapter->GetReceiveWindowSize(), |
| kInitialFlowControlWindowSize + 10000); |
| |
| const std::vector<Header> headers = |
| ToHeaders({{":method", "GET"}, |
| {":scheme", "http"}, |
| {":authority", "example.com"}, |
| {":path", "/this/is/request/one"}}); |
| const int32_t stream_id = adapter->SubmitRequest(headers, nullptr, nullptr); |
| |
| EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); |
| EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); |
| EXPECT_CALL(visitor, OnBeforeFrameSent(WINDOW_UPDATE, 0, 4, 0x0)); |
| EXPECT_CALL(visitor, OnFrameSent(WINDOW_UPDATE, 0, 4, 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); |
| |
| EXPECT_EQ(adapter->GetStreamReceiveWindowSize(stream_id), |
| kInitialFlowControlWindowSize); |
| adapter->SubmitWindowUpdate(1, 20000); |
| EXPECT_EQ(adapter->GetStreamReceiveWindowSize(stream_id), |
| kInitialFlowControlWindowSize + 20000); |
| } |
| |
| TEST(OgHttp2AdapterTest, AckOfSettingInitialWindowSizeAffectsWindow) { |
| DataSavingVisitor visitor; |
| OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; |
| auto adapter = OgHttp2Adapter::Create(visitor, options); |
| |
| testing::InSequence s; |
| |
| const std::vector<Header> headers = |
| ToHeaders({{":method", "GET"}, |
| {":scheme", "http"}, |
| {":authority", "example.com"}, |
| {":path", "/this/is/request/one"}}); |
| const int32_t stream_id1 = adapter->SubmitRequest(headers, nullptr, nullptr); |
| |
| 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); |
| |
| const std::string initial_frames = |
| TestFrameSequence() |
| .ServerPreface() |
| .SettingsAck() // Ack of the client's initial settings. |
| .Serialize(); |
| EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0x0)); |
| EXPECT_CALL(visitor, OnSettingsStart()); |
| EXPECT_CALL(visitor, OnSettingsEnd()); |
| EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0x1)); |
| EXPECT_CALL(visitor, OnSettingsAck); |
| |
| int64_t parse_result = adapter->ProcessBytes(initial_frames); |
| EXPECT_EQ(initial_frames.size(), static_cast<size_t>(parse_result)); |
| |
| EXPECT_EQ(adapter->GetStreamReceiveWindowSize(stream_id1), |
| kInitialFlowControlWindowSize); |
| adapter->SubmitSettings({{INITIAL_WINDOW_SIZE, 80000u}}); |
| // No update for the first stream, yet. |
| EXPECT_EQ(adapter->GetStreamReceiveWindowSize(stream_id1), |
| kInitialFlowControlWindowSize); |
| |
| // Ack of server's initial settings. |
| EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); |
| EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); |
| |
| // Outbound SETTINGS containing INITIAL_WINDOW_SIZE. |
| EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 6, 0x0)); |
| EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 6, 0x0, 0)); |
| |
| result = adapter->Send(); |
| EXPECT_EQ(0, result); |
| |
| // Still no update, as a SETTINGS ack has not yet been received. |
| EXPECT_EQ(adapter->GetStreamReceiveWindowSize(stream_id1), |
| kInitialFlowControlWindowSize); |
| |
| const std::string settings_ack = |
| TestFrameSequence().SettingsAck().Serialize(); |
| |
| EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0x1)); |
| EXPECT_CALL(visitor, OnSettingsAck); |
| |
| parse_result = adapter->ProcessBytes(settings_ack); |
| EXPECT_EQ(settings_ack.size(), static_cast<size_t>(parse_result)); |
| |
| // Stream window has been updated. |
| EXPECT_EQ(adapter->GetStreamReceiveWindowSize(stream_id1), 80000); |
| |
| const std::vector<Header> headers2 = |
| ToHeaders({{":method", "GET"}, |
| {":scheme", "http"}, |
| {":authority", "example.com"}, |
| {":path", "/this/is/request/two"}}); |
| const int32_t stream_id2 = adapter->SubmitRequest(headers, nullptr, nullptr); |
| |
| EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id2, _, 0x5)); |
| EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id2, _, 0x5, 0)); |
| result = adapter->Send(); |
| EXPECT_EQ(0, result); |
| |
| EXPECT_EQ(adapter->GetStreamReceiveWindowSize(stream_id2), 80000); |
| } |
| |
| TEST(OgHttp2AdapterTest, ClientRejects100HeadersWithFin) { |
| DataSavingVisitor visitor; |
| OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; |
| auto adapter = OgHttp2Adapter::Create(visitor, options); |
| |
| testing::InSequence s; |
| |
| const std::vector<Header> headers1 = |
| ToHeaders({{":method", "GET"}, |
| {":scheme", "http"}, |
| {":authority", "example.com"}, |
| {":path", "/this/is/request/one"}}); |
| |
| const int32_t stream_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<size_t>(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<Header> 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<size_t>(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<int>(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<Header> 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<size_t>(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<int>(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<Header> 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<size_t>(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<int>(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<Header> 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<size_t>(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<int>(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<Header> headers = |
| ToHeaders({{":method", "GET"}, |
| {":scheme", "http"}, |
| {":authority", "example.com"}, |
| {":path", "/this/is/request/one"}}); |
| |
| const int32_t stream_id = adapter->SubmitRequest(headers, nullptr, nullptr); |
| ASSERT_GT(stream_id, 0); |
| |
| EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); |
| EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); |
| EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 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<size_t>(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<Header> headers1 = |
| ToHeaders({{":method", "GET"}, |
| {":scheme", "http"}, |
| {":authority", "example.com"}, |
| {":path", "/this/is/request/one"}}); |
| |
| const char* kSentinel1 = "arbitrary pointer 1"; |
| const int32_t stream_id1 = |
| adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1)); |
| ASSERT_GT(stream_id1, 0); |
| QUICHE_LOG(INFO) << "Created stream: " << stream_id1; |
| |
| EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); |
| EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); |
| EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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<size_t>(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<Header> 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<TestDataFrameSource>(visitor, false); |
| body1->AppendPayload(kBody); |
| body1->EndData(); |
| |
| const int32_t stream_id1 = |
| adapter->SubmitRequest(headers1, std::move(body1), nullptr); |
| ASSERT_GT(stream_id1, 0); |
| |
| EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); |
| EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); |
| EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x4)); |
| EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x4, 0)); |
| 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<Header> 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<Header> headers1 = |
| ToHeaders({{":method", "GET"}, |
| {":scheme", "http"}, |
| {":authority", "example.com"}, |
| {":path", "/this/is/request/one"}}); |
| |
| const char* kSentinel1 = "arbitrary pointer 1"; |
| const int32_t stream_id1 = |
| adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1)); |
| ASSERT_GT(stream_id1, 0); |
| QUICHE_LOG(INFO) << "Created stream: " << stream_id1; |
| |
| EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); |
| EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); |
| EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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<size_t>(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<Header> headers1 = |
| ToHeaders({{":method", "GET"}, |
| {":scheme", "http"}, |
| {":authority", "example.com"}, |
| {":path", "/this/is/request/one"}}); |
| |
| const int32_t stream_id = adapter->SubmitRequest(headers1, nullptr, nullptr); |
| ASSERT_GT(stream_id, 0); |
| |
| EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); |
| EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); |
| EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 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<size_t>(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<Header> headers = |
| ToHeaders({{":method", "GET"}, |
| {":scheme", "http"}, |
| {":authority", "example.com"}, |
| {":path", "/this/is/request/one"}}); |
| |
| const int32_t stream_id = adapter->SubmitRequest(headers, nullptr, nullptr); |
| ASSERT_GT(stream_id, 0); |
| |
| EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); |
| EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); |
| EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 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(GOAWAY, 0, _, 0x0)); |
| EXPECT_CALL(visitor, |
| OnFrameSent(GOAWAY, 0, _, 0x0, |
| static_cast<int>(Http2ErrorCode::INTERNAL_ERROR))); |
| |
| EXPECT_FALSE(adapter->want_read()); |
| EXPECT_TRUE(adapter->want_write()); |
| result = adapter->Send(); |
| EXPECT_EQ(0, result); |
| EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::GOAWAY})); |
| } |
| |
| TEST(OgHttp2AdapterTest, ClientHandlesMetadataWithCompletionError) { |
| DataSavingVisitor visitor; |
| OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; |
| auto adapter = OgHttp2Adapter::Create(visitor, options); |
| |
| testing::InSequence s; |
| |
| const std::vector<Header> headers = |
| ToHeaders({{":method", "GET"}, |
| {":scheme", "http"}, |
| {":authority", "example.com"}, |
| {":path", "/this/is/request/one"}}); |
| |
| const int32_t stream_id = adapter->SubmitRequest(headers, nullptr, nullptr); |
| ASSERT_GT(stream_id, 0); |
| |
| EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); |
| EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); |
| EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 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(GOAWAY, 0, _, 0x0)); |
| EXPECT_CALL(visitor, |
| OnFrameSent(GOAWAY, 0, _, 0x0, |
| static_cast<int>(Http2ErrorCode::INTERNAL_ERROR))); |
| |
| EXPECT_FALSE(adapter->want_read()); |
| EXPECT_TRUE(adapter->want_write()); |
| result = adapter->Send(); |
| EXPECT_EQ(0, result); |
| EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::GOAWAY})); |
| } |
| |
| TEST(OgHttp2AdapterTest, ClientRstStreamWhileHandlingHeaders) { |
| DataSavingVisitor visitor; |
| OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; |
| auto adapter = OgHttp2Adapter::Create(visitor, options); |
| |
| testing::InSequence s; |
| |
| const std::vector<Header> headers1 = |
| ToHeaders({{":method", "GET"}, |
| {":scheme", "http"}, |
| {":authority", "example.com"}, |
| {":path", "/this/is/request/one"}}); |
| |
| const char* kSentinel1 = "arbitrary pointer 1"; |
| const int32_t stream_id1 = |
| adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1)); |
| ASSERT_GT(stream_id1, 0); |
| QUICHE_LOG(INFO) << "Created stream: " << stream_id1; |
| |
| EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); |
| EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); |
| EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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<size_t>(stream_result)); |
| |
| EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); |
| EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); |
| EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, stream_id1, 4, 0x0)); |
| EXPECT_CALL(visitor, |
| OnFrameSent(RST_STREAM, stream_id1, 4, 0x0, |
| static_cast<int>(Http2ErrorCode::REFUSED_STREAM))); |
| EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::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<Header> headers1 = |
| ToHeaders({{":method", "GET"}, |
| {":scheme", "http"}, |
| {":authority", "example.com"}, |
| {":path", "/this/is/request/one"}}); |
| |
| const char* kSentinel1 = "arbitrary pointer 1"; |
| const int32_t stream_id1 = |
| adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1)); |
| ASSERT_GT(stream_id1, 0); |
| QUICHE_LOG(INFO) << "Created stream: " << stream_id1; |
| |
| EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); |
| EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); |
| EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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(GOAWAY, 0, _, 0x0)); |
| EXPECT_CALL(visitor, |
| OnFrameSent(GOAWAY, 0, _, 0x0, |
| static_cast<int>(Http2ErrorCode::INTERNAL_ERROR))); |
| |
| EXPECT_TRUE(adapter->want_write()); |
| result = adapter->Send(); |
| EXPECT_EQ(0, result); |
| EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::GOAWAY})); |
| } |
| |
| TEST(OgHttp2AdapterTest, ClientConnectionErrorWhileHandlingHeadersOnly) { |
| DataSavingVisitor visitor; |
| OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; |
| auto adapter = OgHttp2Adapter::Create(visitor, options); |
| |
| testing::InSequence s; |
| |
| const std::vector<Header> headers1 = |
| ToHeaders({{":method", "GET"}, |
| {":scheme", "http"}, |
| {":authority", "example.com"}, |
| {":path", "/this/is/request/one"}}); |
| |
| const char* kSentinel1 = "arbitrary pointer 1"; |
| const int32_t stream_id1 = |
| adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1)); |
| ASSERT_GT(stream_id1, 0); |
| QUICHE_LOG(INFO) << "Created stream: " << stream_id1; |
| |
| EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); |
| EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); |
| EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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(GOAWAY, 0, _, 0x0)); |
| EXPECT_CALL(visitor, |
| OnFrameSent(GOAWAY, 0, _, 0x0, |
| static_cast<int>(Http2ErrorCode::INTERNAL_ERROR))); |
| |
| EXPECT_TRUE(adapter->want_write()); |
| result = adapter->Send(); |
| EXPECT_EQ(0, result); |
| EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::GOAWAY})); |
| } |
| |
| TEST(OgHttp2AdapterTest, ClientRejectsHeaders) { |
| DataSavingVisitor visitor; |
| OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; |
| auto adapter = OgHttp2Adapter::Create(visitor, options); |
| |
| testing::InSequence s; |
| |
| const std::vector<Header> headers1 = |
| ToHeaders({{":method", "GET"}, |
| {":scheme", "http"}, |
| {":authority", "example.com"}, |
| {":path", "/this/is/request/one"}}); |
| |
| const char* kSentinel1 = "arbitrary pointer 1"; |
| const int32_t stream_id1 = |
| adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1)); |
| ASSERT_GT(stream_id1, 0); |
| QUICHE_LOG(INFO) << "Created stream: " << stream_id1; |
| |
| EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); |
| EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); |
| EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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(GOAWAY, 0, _, 0x0)); |
| EXPECT_CALL(visitor, |
| OnFrameSent(GOAWAY, 0, _, 0x0, |
| static_cast<int>(Http2ErrorCode::INTERNAL_ERROR))); |
| |
| EXPECT_TRUE(adapter->want_write()); |
| result = adapter->Send(); |
| EXPECT_EQ(0, result); |
| EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::GOAWAY})); |
| } |
| |
| TEST(OgHttp2AdapterTest, ClientHandlesSmallerHpackHeaderTableSetting) { |
| DataSavingVisitor visitor; |
| OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; |
| auto adapter = OgHttp2Adapter::Create(visitor, options); |
| |
| testing::InSequence s; |
| |
| const std::vector<Header> headers1 = ToHeaders({ |
| {":method", "GET"}, |
| {":scheme", "http"}, |
| {":authority", "example.com"}, |
| {":path", "/this/is/request/one"}, |
| {"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<size_t>(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<size_t>(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<Header> 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<size_t>(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<Header> 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<size_t>(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<size_t>(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<Header> headers1 = |
| ToHeaders({{":method", "GET"}, |
| {":scheme", "http"}, |
| {":authority", "example.com"}, |
| {":path", "/this/is/request/one"}}); |
| |
| const char* kSentinel1 = "arbitrary pointer 1"; |
| const int32_t stream_id1 = |
| adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1)); |
| ASSERT_GT(stream_id1, 0); |
| QUICHE_LOG(INFO) << "Created stream: " << stream_id1; |
| |
| EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5)); |
| EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0)); |
| |
| int result = adapter->Send(); |
| EXPECT_EQ(0, result); |
| absl::string_view data = visitor.data(); |
| EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); |
| data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); |
| EXPECT_THAT(data, |
| EqualsFrames({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<size_t>(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<Header> headers1 = |
| ToHeaders({{":method", "GET"}, |
| {":scheme", "http"}, |
| {":authority", "example.com"}, |
| {":path", "/this/is/request/one"}}); |
| |
| const char* kSentinel1 = "arbitrary pointer 1"; |
| const int32_t stream_id1 = |
| adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1)); |
| ASSERT_GT(stream_id1, 0); |
| QUICHE_LOG(INFO) << "Created stream: " << stream_id1; |
| |
| EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); |
| EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); |
| EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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(GOAWAY, 0, _, 0x0)); |
| EXPECT_CALL(visitor, |
| OnFrameSent(GOAWAY, 0, _, 0x0, |
| static_cast<int>(Http2ErrorCode::INTERNAL_ERROR))); |
| |
| EXPECT_TRUE(adapter->want_write()); |
| result = adapter->Send(); |
| EXPECT_EQ(0, result); |
| EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::GOAWAY})); |
| } |
| |
| TEST(OgHttp2AdapterTest, ClientRejects101Response) { |
| DataSavingVisitor visitor; |
| OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; |
| auto adapter = OgHttp2Adapter::Create(visitor, options); |
| |
| testing::InSequence s; |
| |
| const std::vector<Header> headers1 = |
| ToHeaders({{":method", "GET"}, |
| {":scheme", "http"}, |
| {":authority", "example.com"}, |
| {":path", "/this/is/request/one"}, |
| {"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<int64_t>(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<uint32_t>(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<size_t>(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<TestDataFrameSource>(visitor, true); |
| body1->AppendPayload(kBody); |
| body1->EndData(); |
| const int stream_id = |
| adapter->SubmitRequest(ToHeaders({{":method", "POST"}, |
| {":scheme", "http"}, |
| {":authority", "example.com"}, |
| {":path", "/this/is/request/one"}}), |
| std::move(body1), nullptr); |
| EXPECT_GT(stream_id, 0); |
| EXPECT_TRUE(adapter->want_write()); |
| |
| EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4)); |
| EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0)); |
| EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, _, 0x1, 0)); |
| |
| result = adapter->Send(); |
| EXPECT_EQ(0, result); |
| EXPECT_THAT(visitor.data(), |
| EqualsFrames({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<size_t>(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<size_t>(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<TestDataFrameSource>(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<size_t>(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<TestDataFrameSource>(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<size_t>(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<size_t>(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(GOAWAY, 0, _, 0x0)); |
| EXPECT_CALL( |
| visitor, |
| OnFrameSent(GOAWAY, 0, _, 0x0, |
| static_cast<int>(Http2ErrorCode::FLOW_CONTROL_ERROR))); |
| |
| 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::GOAWAY})); |
| 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<Header> headers = |
| ToHeaders({{":method", "GET"}, |
| {":scheme", "http"}, |
| {":authority", "example.com"}, |
| {":path", "/this/is/request/one"}}); |
| const int32_t stream_id = adapter->SubmitRequest(headers, nullptr, nullptr); |
| ASSERT_GT(stream_id, 0); |
| |
| EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); |
| EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); |
| EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 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<size_t>(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<int>(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<Header> headers = |
| ToHeaders({{":method", "GET"}, |
| {":scheme", "http"}, |
| {":authority", "example.com"}, |
| {":path", "/this/is/request/one"}}); |
| const int32_t stream_id = adapter->SubmitRequest(headers, nullptr, nullptr); |
|