blob: 077e33c3ec3120b4301b20137c890b2f6f0b4198 [file] [log] [blame]
#include "quiche/http2/adapter/oghttp2_adapter.h"
#include <string>
#include "absl/strings/str_join.h"
#include "quiche/http2/adapter/http2_protocol.h"
#include "quiche/http2/adapter/http2_visitor_interface.h"
#include "quiche/http2/adapter/mock_http2_visitor.h"
#include "quiche/http2/adapter/oghttp2_util.h"
#include "quiche/http2/adapter/test_frame_sequence.h"
#include "quiche/http2/adapter/test_utils.h"
#include "quiche/common/platform/api/quiche_expect_bug.h"
#include "quiche/common/platform/api/quiche_test.h"
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;
options.perspective = Perspective::kServer;
auto adapter = OgHttp2Adapter::Create(visitor, options);
EXPECT_TRUE(adapter->IsServerSession());
}
TEST(OgHttp2AdapterTest, ProcessBytes) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options 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, HeaderValuesWithObsTextAllowedByDefault) {
DataSavingVisitor visitor;
OgHttp2Session::Options options;
options.perspective = Perspective::kServer;
ASSERT_TRUE(options.allow_obs_text);
auto adapter = OgHttp2Adapter::Create(visitor, options);
const std::string frames = TestFrameSequence()
.ClientPreface()
.Headers(1,
{{":method", "GET"},
{":scheme", "https"},
{":authority", "example.com"},
{":path", "/"},
{"name", "val\xa1ue"}},
/*fin=*/true)
.Serialize();
testing::InSequence s;
// Client preface (empty SETTINGS)
EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
EXPECT_CALL(visitor, OnSettingsStart());
EXPECT_CALL(visitor, OnSettingsEnd());
// Stream 1
EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5));
EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "GET"));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/"));
EXPECT_CALL(visitor, OnHeaderForStream(1, "name", "val\xa1ue"));
EXPECT_CALL(visitor, OnEndHeadersForStream(1));
EXPECT_CALL(visitor, OnEndStream(1));
const int64_t result = adapter->ProcessBytes(frames);
EXPECT_EQ(frames.size(), static_cast<size_t>(result));
}
TEST(OgHttp2AdapterTest, HeaderValuesWithObsTextDisallowed) {
DataSavingVisitor visitor;
OgHttp2Session::Options options;
options.allow_obs_text = false;
options.perspective = Perspective::kServer;
auto adapter = OgHttp2Adapter::Create(visitor, options);
const std::string frames = TestFrameSequence()
.ClientPreface()
.Headers(1,
{{":method", "GET"},
{":scheme", "https"},
{":authority", "example.com"},
{":path", "/"},
{"name", "val\xa1ue"}},
/*fin=*/true)
.Serialize();
testing::InSequence s;
// Client preface (empty SETTINGS)
EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
EXPECT_CALL(visitor, OnSettingsStart());
EXPECT_CALL(visitor, OnSettingsEnd());
// Stream 1
EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5));
EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "GET"));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/"));
EXPECT_CALL(
visitor,
OnInvalidFrame(1, Http2VisitorInterface::InvalidFrameError::kHttpHeader));
const int64_t result = adapter->ProcessBytes(frames);
EXPECT_EQ(frames.size(), static_cast<size_t>(result));
}
TEST(OgHttp2AdapterTest, InitialSettingsNoExtendedConnect) {
DataSavingVisitor client_visitor;
OgHttp2Adapter::Options client_options;
client_options.perspective = Perspective::kClient;
client_options.max_header_list_bytes = 42;
client_options.allow_extended_connect = false;
auto client_adapter = OgHttp2Adapter::Create(client_visitor, client_options);
DataSavingVisitor server_visitor;
OgHttp2Adapter::Options server_options;
server_options.perspective = Perspective::kServer;
server_options.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}));
EXPECT_CALL(
server_visitor,
OnSetting(Http2Setting{Http2KnownSettingsId::MAX_HEADER_LIST_SIZE, 42u}));
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;
client_options.perspective = Perspective::kClient;
client_options.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;
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}));
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}));
EXPECT_CALL(
server_visitor,
OnSetting(Http2Setting{Http2KnownSettingsId::MAX_HEADER_LIST_SIZE, 42u}));
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;
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;
options.perspective = Perspective::kServer;
options.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;
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;
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;
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;
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;
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;
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;
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);
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(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));
EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id2, _, 0x5));
EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id2, _, 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")
.Headers(3, {{":status", "204"}}, /*fin=*/false)
.Data(3, "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));
EXPECT_CALL(visitor, OnFrameHeader(3, _, HEADERS, 4));
EXPECT_CALL(visitor, OnBeginHeadersForStream(3));
EXPECT_CALL(visitor, OnHeaderForStream(3, ":status", "204"));
EXPECT_CALL(visitor, OnEndHeadersForStream(3));
EXPECT_CALL(visitor, OnFrameHeader(3, _, DATA, 0));
EXPECT_CALL(visitor, OnBeginDataForStream(3, 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_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 3, _, 0x0));
EXPECT_CALL(visitor,
OnFrameSent(RST_STREAM, 3, _, 0x0,
static_cast<int>(Http2ErrorCode::PROTOCOL_ERROR)));
EXPECT_CALL(visitor, OnCloseStream(3, 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,
SpdyFrameType::RST_STREAM}));
}
TEST(OgHttp2AdapterTest, ClientHandles304WithContent) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options;
options.perspective = Perspective::kClient;
auto adapter = OgHttp2Adapter::Create(visitor, options);
testing::InSequence s;
const std::vector<Header> headers1 =
ToHeaders({{":method", "GET"},
{":scheme", "http"},
{":authority", "example.com"},
{":path", "/this/is/request/one"}});
const int32_t stream_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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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}));
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;
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}));
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;
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;
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;
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, ClientReceivesGoAway) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options;
options.perspective = Perspective::kClient;
auto adapter = OgHttp2Adapter::Create(visitor, options);
testing::InSequence s;
const std::vector<Header> headers1 =
ToHeaders({{":method", "GET"},
{":scheme", "http"},
{":authority", "example.com"},
{":path", "/this/is/request/one"}});
const int32_t stream_id1 = adapter->SubmitRequest(headers1, nullptr, nullptr);
ASSERT_GT(stream_id1, 0);
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(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));
EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id2, _, 0x5));
EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id2, _, 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,
SpdyFrameType::HEADERS}));
visitor.Clear();
// Submit a pending WINDOW_UPDATE for a stream that will be closed due to
// GOAWAY. The WINDOW_UPDATE should not be sent.
adapter->SubmitWindowUpdate(3, 42);
const std::string stream_frames =
TestFrameSequence()
.ServerPreface()
.RstStream(1, Http2ErrorCode::ENHANCE_YOUR_CALM)
.GoAway(1, Http2ErrorCode::INTERNAL_ERROR, "indigestion")
.WindowUpdate(0, 42)
.WindowUpdate(1, 42)
.Serialize();
EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
EXPECT_CALL(visitor, OnSettingsStart());
EXPECT_CALL(visitor, OnSettingsEnd());
EXPECT_CALL(visitor, OnFrameHeader(1, 4, RST_STREAM, 0));
EXPECT_CALL(visitor, OnRstStream(1, Http2ErrorCode::ENHANCE_YOUR_CALM));
EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::ENHANCE_YOUR_CALM));
EXPECT_CALL(visitor, OnFrameHeader(0, _, GOAWAY, 0));
// Currently, oghttp2 does not pass the opaque data to the visitor.
EXPECT_CALL(visitor, OnGoAway(1, Http2ErrorCode::INTERNAL_ERROR, ""));
EXPECT_CALL(visitor, OnCloseStream(3, Http2ErrorCode::REFUSED_STREAM));
EXPECT_CALL(visitor, OnFrameHeader(0, 4, WINDOW_UPDATE, 0));
EXPECT_CALL(visitor, OnWindowUpdate(0, 42));
EXPECT_CALL(visitor, OnFrameHeader(1, 4, WINDOW_UPDATE, 0));
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_TRUE(adapter->want_write());
result = adapter->Send();
EXPECT_EQ(0, result);
EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS}));
}
TEST(OgHttp2AdapterTest, ClientReceivesMultipleGoAways) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options;
options.perspective = Perspective::kClient;
auto adapter = OgHttp2Adapter::Create(visitor, options);
testing::InSequence s;
const std::vector<Header> headers1 =
ToHeaders({{":method", "GET"},
{":scheme", "http"},
{":authority", "example.com"},
{":path", "/this/is/request/one"}});
const int32_t stream_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 initial_frames =
TestFrameSequence()
.ServerPreface()
.GoAway(kMaxStreamId, Http2ErrorCode::INTERNAL_ERROR, "indigestion")
.Serialize();
EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
EXPECT_CALL(visitor, OnSettingsStart());
EXPECT_CALL(visitor, OnSettingsEnd());
EXPECT_CALL(visitor, OnFrameHeader(0, _, GOAWAY, 0));
// Currently, oghttp2 does not pass the opaque data to the visitor.
EXPECT_CALL(visitor,
OnGoAway(kMaxStreamId, Http2ErrorCode::INTERNAL_ERROR, ""));
const int64_t initial_result = adapter->ProcessBytes(initial_frames);
EXPECT_EQ(initial_frames.size(), static_cast<size_t>(initial_result));
// Submit a WINDOW_UPDATE for the open stream. Because the stream is below the
// GOAWAY's last_stream_id, it should be sent.
adapter->SubmitWindowUpdate(1, 42);
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(WINDOW_UPDATE, 1, 4, 0x0));
EXPECT_CALL(visitor, OnFrameSent(WINDOW_UPDATE, 1, 4, 0x0, 0));
result = adapter->Send();
EXPECT_EQ(0, result);
EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS,
SpdyFrameType::WINDOW_UPDATE}));
visitor.Clear();
const std::string final_frames =
TestFrameSequence()
.GoAway(0, Http2ErrorCode::INTERNAL_ERROR, "indigestion")
.Serialize();
EXPECT_CALL(visitor, OnFrameHeader(0, _, GOAWAY, 0));
// Currently, oghttp2 does not pass the opaque data to the visitor.
EXPECT_CALL(visitor, OnGoAway(0, Http2ErrorCode::INTERNAL_ERROR, ""));
EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::REFUSED_STREAM));
const int64_t final_result = adapter->ProcessBytes(final_frames);
EXPECT_EQ(final_frames.size(), static_cast<size_t>(final_result));
EXPECT_FALSE(adapter->want_write());
result = adapter->Send();
EXPECT_EQ(0, result);
EXPECT_THAT(visitor.data(), testing::IsEmpty());
}
TEST(OgHttp2AdapterTest, ClientReceivesMultipleGoAwaysWithIncreasingStreamId) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options;
options.perspective = Perspective::kClient;
auto adapter = OgHttp2Adapter::Create(visitor, options);
testing::InSequence s;
const std::vector<Header> headers1 =
ToHeaders({{":method", "GET"},
{":scheme", "http"},
{":authority", "example.com"},
{":path", "/this/is/request/one"}});
const int32_t stream_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 frames =
TestFrameSequence()
.ServerPreface()
.GoAway(0, Http2ErrorCode::HTTP2_NO_ERROR, "")
.GoAway(0, Http2ErrorCode::ENHANCE_YOUR_CALM, "")
.GoAway(1, Http2ErrorCode::INTERNAL_ERROR, "")
.Serialize();
EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
EXPECT_CALL(visitor, OnSettingsStart());
EXPECT_CALL(visitor, OnSettingsEnd());
EXPECT_CALL(visitor, OnFrameHeader(0, _, GOAWAY, 0));
EXPECT_CALL(visitor, OnGoAway(0, Http2ErrorCode::HTTP2_NO_ERROR, ""));
EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::REFUSED_STREAM));
EXPECT_CALL(visitor, OnFrameHeader(0, _, GOAWAY, 0));
EXPECT_CALL(visitor, OnGoAway(0, Http2ErrorCode::ENHANCE_YOUR_CALM, ""));
EXPECT_CALL(visitor, OnFrameHeader(0, _, GOAWAY, 0));
EXPECT_CALL(
visitor,
OnInvalidFrame(0, Http2VisitorInterface::InvalidFrameError::kProtocol));
// The oghttp2 stack also signals the error via OnConnectionError().
EXPECT_CALL(visitor,
OnConnectionError(ConnectionError::kInvalidGoAwayLastStreamId));
const int64_t frames_result = adapter->ProcessBytes(frames);
EXPECT_EQ(frames.size(), static_cast<size_t>(frames_result));
EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0));
EXPECT_CALL(visitor,
OnFrameSent(GOAWAY, 0, _, 0x0,
static_cast<int>(Http2ErrorCode::PROTOCOL_ERROR)));
EXPECT_TRUE(adapter->want_write());
result = adapter->Send();
EXPECT_EQ(0, result);
EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::GOAWAY}));
}
TEST(OgHttp2AdapterTest, ClientReceivesGoAwayWithPendingStreams) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options 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);
absl::string_view data = visitor.data();
EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
EXPECT_THAT(data, EqualsFrames({SpdyFrameType::SETTINGS}));
visitor.Clear();
const std::string initial_frames =
TestFrameSequence()
.ServerPreface({{MAX_CONCURRENT_STREAMS, 1}})
.Serialize();
// Server preface (SETTINGS with MAX_CONCURRENT_STREAMS)
EXPECT_CALL(visitor, OnFrameHeader(0, 6, SETTINGS, 0));
EXPECT_CALL(visitor, OnSettingsStart());
EXPECT_CALL(visitor, OnSetting);
EXPECT_CALL(visitor, OnSettingsEnd());
const int64_t initial_result = adapter->ProcessBytes(initial_frames);
EXPECT_EQ(initial_frames.size(), static_cast<size_t>(initial_result));
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);
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);
// The second request should be pending because of
// SETTINGS_MAX_CONCURRENT_STREAMS.
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
result = adapter->Send();
EXPECT_EQ(0, result);
EXPECT_THAT(visitor.data(),
EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::HEADERS}));
visitor.Clear();
// Let the client receive a GOAWAY and raise MAX_CONCURRENT_STREAMS. Even
// though the GOAWAY last_stream_id is higher than the pending request's
// stream ID, pending request should not be sent.
const std::string stream_frames =
TestFrameSequence()
.GoAway(kMaxStreamId, Http2ErrorCode::INTERNAL_ERROR, "indigestion")
.Settings({{MAX_CONCURRENT_STREAMS, 42u}})
.Serialize();
EXPECT_CALL(visitor, OnFrameHeader(0, _, GOAWAY, 0));
EXPECT_CALL(visitor,
OnGoAway(kMaxStreamId, Http2ErrorCode::INTERNAL_ERROR, ""));
EXPECT_CALL(visitor, OnFrameHeader(0, 6, SETTINGS, 0));
EXPECT_CALL(visitor, OnSettingsStart());
EXPECT_CALL(visitor, OnSetting(Http2Setting{MAX_CONCURRENT_STREAMS, 42u}));
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_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
// We close the pending stream on the next write attempt.
EXPECT_CALL(visitor, OnCloseStream(3, Http2ErrorCode::REFUSED_STREAM));
EXPECT_TRUE(adapter->want_write());
result = adapter->Send();
EXPECT_EQ(0, result);
EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS}));
visitor.Clear();
// Requests submitted after receiving the GOAWAY should not be sent.
const std::vector<Header> headers3 =
ToHeaders({{":method", "GET"},
{":scheme", "http"},
{":authority", "example.com"},
{":path", "/this/is/request/three"}});
const int32_t stream_id3 = adapter->SubmitRequest(headers3, nullptr, nullptr);
ASSERT_GT(stream_id3, stream_id2);
// We close the pending stream on the next write attempt.
EXPECT_CALL(visitor, OnCloseStream(5, Http2ErrorCode::REFUSED_STREAM));
EXPECT_TRUE(adapter->want_write());
result = adapter->Send();
EXPECT_EQ(0, result);
EXPECT_THAT(visitor.data(), testing::IsEmpty());
EXPECT_FALSE(adapter->want_write());
}
TEST(OgHttp2AdapterTest, ClientFailsOnGoAway) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options;
options.perspective = Perspective::kClient;
auto adapter = OgHttp2Adapter::Create(visitor, options);
testing::InSequence s;
const std::vector<Header> headers1 =
ToHeaders({{":method", "GET"},
{":scheme", "http"},
{":authority", "example.com"},
{":path", "/this/is/request/one"}});
const char* kSentinel1 = "arbitrary pointer 1";
const int32_t stream_id1 =
adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1));
ASSERT_GT(stream_id1, 0);
QUICHE_LOG(INFO) << "Created stream: " << stream_id1;
EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 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;
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_fra